Skip to content

Commit 087837d

Browse files
authored
Include asis in add and update methods (#10)
- Include `asis` as an option, It existed in the earlier versions but at one point there was a regression. - Add tests - Revamp docstrings
1 parent eb37d35 commit 087837d

File tree

4 files changed

+132
-58
lines changed

4 files changed

+132
-58
lines changed

src/pybiocfilecache/BiocFileCache.py

+81-38
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33
import os
44
from pathlib import Path
55
from time import sleep, time
6-
from typing import List, Optional, Union
6+
from typing import List, Literal, Optional, Union
77

88
from sqlalchemy import func
99
from sqlalchemy.orm import Session
1010

11+
from ._exceptions import NoFpathError, RnameExistsError, RpathTimeoutError
1112
from .db import create_schema
1213
from .db.schema import Resource
1314
from .utils import copy_or_move, create_tmp_dir, generate_id
14-
from ._exceptions import NoFpathError, RnameExistsError, RpathTimeoutError
1515

16-
__author__ = "jkanche"
16+
__author__ = "Jayaram Kancherla"
1717
__copyright__ = "jkanche"
1818
__license__ = "MIT"
1919

@@ -25,8 +25,10 @@ def __init__(self, cacheDirOrPath: Union[str, Path] = create_tmp_dir()):
2525
"""Initialize BiocFileCache.
2626
2727
Args:
28-
cacheDirOrPath (Union[str, Path], optional): Path to cache.
29-
directory. Defaults to tmp location, `create_tmp_dir()`.
28+
cacheDirOrPath:
29+
Path to cache directory.
30+
31+
Defaults to tmp location, :py:func:`~.utils.create_tmp_dir`.
3032
3133
Raises:
3234
Exception: Failed to initialize cache.
@@ -51,38 +53,54 @@ def add(
5153
self,
5254
rname: str,
5355
fpath: Union[str, Path],
54-
rtype: str = "local",
55-
action: str = "copy",
56+
rtype: Literal["local", "web", "relative"] = "local",
57+
action: Literal["copy", "move", "asis"] = "copy",
5658
ext: bool = False,
5759
) -> Resource:
5860
"""Add a resource from the provided `fpath` to cache as `rname`.
5961
6062
Args:
61-
rname (str): Name of the resource to add to cache.
62-
fpath (Union[str, Path]): Location of the resource.
63-
rtype (str, optional): One of `"local"`, `"web"`, or `"relative"`.
64-
Defaults to `"local"`.
65-
action (str, optional): Either `"copy"`, `"move"` or `"asis"`.
66-
Defaults to `"copy"`.
67-
ext (bool, optional): Use filepath extension when storing in cache.
68-
Defaults to `False`.
63+
rname:
64+
Name of the resource to add to cache.
6965
70-
Returns:
71-
Resource: Database record of the new resource in cache.
66+
fpath:
67+
Location of the resource.
68+
69+
rtype:
70+
One of ``local``, ``web``, or ``relative``.
71+
Defaults to ``local``.
72+
73+
action:
74+
Either ``copy``, ``move`` or ``asis``.
75+
Defaults to ``copy``.
76+
77+
ext:
78+
Whether to use filepath extension when storing in cache.
79+
Defaults to `False`.
7280
7381
Raises:
74-
NoFpathError: When the `fpath` does not exist.
75-
RnameExistsError: When the `rname` already exists in the cache.
76-
sqlalchemy exceptions: When something is up with the cache.
82+
NoFpathError:
83+
When the `fpath` does not exist.
84+
85+
RnameExistsError:
86+
When the `rname` already exists in the cache.
87+
sqlalchemy exceptions: When something is up with the cache.
88+
89+
Returns:
90+
Database record of the new resource in cache.
7791
"""
7892
if isinstance(fpath, str):
7993
fpath = Path(fpath)
8094

8195
if not fpath.exists():
82-
raise NoFpathError(f"Resource at {fpath} does not exist.")
96+
raise NoFpathError(f"Resource at '{fpath}' does not exist.")
8397

8498
rid = generate_id()
85-
rpath = f"{self.cache}/{rid}" + (f".{fpath.suffix}" if ext else "")
99+
rpath = (
100+
f"{self.cache}/{rid}" + (f".{fpath.suffix}" if ext else "")
101+
if action != "asis"
102+
else str(fpath)
103+
)
86104

87105
# create new record in the database
88106
res = Resource(
@@ -123,11 +141,15 @@ def query(self, query: str, field: str = "rname") -> List[Resource]:
123141
"""Search cache for a resource.
124142
125143
Args:
126-
query (str): query or keywords to search.
127-
field (str, optional): Field to search. Defaults to "rname".
144+
query:
145+
Query string or keywords to search.
146+
147+
field:
148+
Field to search.
149+
Defaults to "rname".
128150
129151
Returns:
130-
List[Resource]: list of matching resources from cache.
152+
List of matching resources from cache.
131153
"""
132154
with self.sessionLocal() as session:
133155
return (
@@ -140,11 +162,14 @@ def _get(self, session: Session, rname: str) -> Optional[Resource]:
140162
"""Get a resource with `rname` from given `Session`.
141163
142164
Args:
143-
session (Session): The `Session` object to use.
144-
rname (str): The `rname` of the `Resource` to get.
165+
session:
166+
The `Session` object to use.
167+
168+
rname:
169+
The `rname` of the `Resource` to get.
145170
146171
Returns:
147-
(Resource, optional): The `Resource` for the `rname` if any.
172+
The `Resource` for the `rname` if available.
148173
"""
149174
resource: Optional[Resource] = (
150175
session.query(Resource).filter(Resource.rname == rname).first()
@@ -169,18 +194,20 @@ def get(self, rname: str) -> Optional[Resource]:
169194
"""Get resource by name from cache.
170195
171196
Args:
172-
rname (str): Name of the file to search.
197+
rname:
198+
Name of the file to search.
173199
174200
Returns:
175-
Optional[Resource]: matched resource from cache if exists.
201+
Matched `Resource` from cache if exists.
176202
"""
177203
return self._get(self.sessionLocal(), rname)
178204

179205
def remove(self, rname: str) -> None:
180206
"""Remove a resource from cache by name.
181207
182208
Args:
183-
rname (str): Name of the resource to remove.
209+
rname:
210+
Name of the resource to remove.
184211
"""
185212
with self.sessionLocal() as session:
186213
res: Optional[Resource] = self._get(session, rname)
@@ -196,18 +223,30 @@ def purge(self):
196223
for file in os.scandir(self.cache):
197224
os.remove(file.path)
198225

226+
return True
227+
199228
def update(
200-
self, rname: str, fpath: Union[str, Path], action: str = "copy"
229+
self,
230+
rname: str,
231+
fpath: Union[str, Path],
232+
action: Literal["copy", "move", "asis"] = "copy",
201233
) -> Resource:
202234
"""Update a resource in cache.
203235
204236
Args:
205-
rname (str): name of the resource in cache.
206-
fpath (Union[str, Path]): new resource to replace existing file in cache.
207-
action (str, optional): either copy of move. defaults to copy.
237+
rname:
238+
Name of the resource in cache.
239+
240+
fpath:
241+
New resource to replace existing file in cache.
242+
243+
action:
244+
Either ``copy``, ``move`` or ``asis``.
245+
246+
Defaults to ``copy``.
208247
209248
Returns:
210-
Resource: Updated resource record in cache.
249+
Updated resource record in cache.
211250
"""
212251

213252
if isinstance(fpath, str):
@@ -220,8 +259,12 @@ def update(
220259
res = self._get(session, rname)
221260

222261
if res is not None:
223-
# copy the file to cache
224-
copy_or_move(str(fpath), str(res.rpath), rname, action)
262+
if action != "asis":
263+
# copy the file to cache
264+
copy_or_move(str(fpath), str(res.rpath), rname, action)
265+
else:
266+
res.rpath = str(fpath)
267+
225268
res.access_time = res.last_modified_time = func.now()
226269
session.merge(res)
227270
session.commit()

src/pybiocfilecache/db/Base.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
from typing import Tuple
2+
13
from sqlalchemy import create_engine
24
from sqlalchemy.engine import Engine
35

46
# from sqlalchemy.ext.declarative import declarative_base
5-
from sqlalchemy.orm import sessionmaker, declarative_base
6-
from typing import Tuple
7+
from sqlalchemy.orm import declarative_base, sessionmaker
78

89
__author__ = "jkanche"
910
__copyright__ = "jkanche"
@@ -16,10 +17,11 @@ def create_schema(cache_dir: str) -> Tuple[Engine, sessionmaker]:
1617
"""Create the schema in the sqlite database.
1718
1819
Args:
19-
cache_dir (str): Location where the cache directory
20+
cache_dir:
21+
Location where the cache directory.
2022
2123
Returns:
22-
a tuple of sqlalchemy engine and session maker
24+
A tuple of sqlalchemy engine and session maker.
2325
"""
2426
try:
2527
engine = create_engine(

src/pybiocfilecache/utils.py

+37-16
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1+
import logging
2+
import sys
13
import tempfile
24
import uuid
35
from pathlib import Path
46
from shutil import copy2, move
7+
from typing import Literal, Union
58

6-
from typing import Union
7-
import logging
8-
import sys
9+
__author__ = "Jayaram Kancherla"
10+
__copyright__ = "jkanche"
11+
__license__ = "MIT"
912

1013

1114
def create_tmp_dir() -> str:
1215
"""Create a temporary directory.
1316
1417
Returns:
15-
str: path to the directory
18+
Temporary path to the directory.
1619
"""
1720
return tempfile.mkdtemp()
1821

@@ -21,38 +24,56 @@ def generate_id() -> str:
2124
"""Generate uuid.
2225
2326
Returns:
24-
str: unique string for use as id
27+
Unique string for use as id.
2528
"""
2629
return uuid.uuid4().hex
2730

2831

2932
def copy_or_move(
30-
source: Union[str, Path], target: Union[str, Path], rname: str, action: str = "copy"
33+
source: Union[str, Path],
34+
target: Union[str, Path],
35+
rname: str,
36+
action: Literal["copy", "move", "asis"] = "copy",
3137
) -> None:
32-
"""Copy or move a resource from `source` to `target`
38+
"""Copy or move a resource from ``source`` to ``target``.
3339
3440
Args:
35-
source (Union[str, Path]): source location of the resource to copy of move.
36-
target (Union[str, Path]): destination to copy of move to.
37-
rname (str): Name of resource to add to cache
38-
action (str): copy of move file from source. Defaults to copy.
41+
source:
42+
Source location of the resource to copy of move.
43+
44+
target:
45+
Destination to copy of move to.
46+
47+
rname:
48+
Name of resource to add to cache.
49+
50+
action:
51+
Copy of move file from source.
52+
Defaults to copy.
3953
4054
Raises:
41-
ValueError: if action is not `copy` or `move`.
42-
Exception: Error storing resource in the cache directory.
55+
ValueError:
56+
If action is not `copy`, `move` or `asis`.
57+
58+
Exception:
59+
Error storing resource in the cache directory.
4360
"""
4461

45-
if action not in ["copy", "move"]:
46-
raise ValueError(f"Action must be either 'move' or 'copy', provided {action}")
62+
if action not in ["copy", "move", "asis"]:
63+
raise ValueError(
64+
f"Action must be either 'move', 'copy' or 'asis', provided {action}."
65+
)
4766

4867
try:
4968
if action == "copy":
5069
copy2(source, target)
5170
elif action == "move":
5271
move(str(source), target)
72+
elif action == "asis":
73+
pass
5374
except Exception as e:
5475
raise Exception(
55-
f"Error storing resource: '{rname}' from: '{source}' in '{target}'",
76+
f"Error storing resource: '{rname}' from: '{source}' in '{target}'.",
5677
) from e
5778

5879

tests/test_cache.py

+8
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ def test_add_get_operations():
3333
frec2 = open(rec2.rpath, "r").read().strip()
3434
assert frec2 == "test2"
3535

36+
bfc.add("test3_asis", os.getcwd() + "/tests/data/test2.txt", action="asis")
37+
rec3 = bfc.get("test3_asis")
38+
assert rec3 is not None
39+
assert rec3.rpath == os.getcwd() + "/tests/data/test2.txt"
40+
41+
frec3 = open(rec3.rpath, "r").read().strip()
42+
assert frec3 == "test2"
43+
3644
bfc.purge()
3745

3846

0 commit comments

Comments
 (0)