|
| 1 | +"""Local file controller. |
| 2 | +
|
| 3 | +The implementation moved to ``opensemantic.core``, which owns the file models. |
| 4 | +This subclass keeps the ``path`` argument and the filename-derived label of the |
| 5 | +controller it replaces. |
| 6 | +""" |
| 7 | + |
1 | 8 | import os |
2 | | -import shutil |
3 | | -from io import TextIOBase |
4 | 9 | from pathlib import Path |
5 | | -from typing import IO, Any, Dict, List, Optional |
| 10 | +from typing import IO, Any, Dict, Optional |
| 11 | + |
| 12 | +from opensemantic.core.v1 import LocalFileController as _LocalFileController |
6 | 13 |
|
7 | | -from osw.controller.file.base import FileController |
8 | 14 | from osw.core import model |
9 | 15 |
|
10 | 16 |
|
11 | | -class LocalFileController(FileController, model.LocalFile): |
| 17 | +class LocalFileController(_LocalFileController): |
12 | 18 | """File controller for local files""" |
13 | 19 |
|
14 | | - label: Optional[List[model.Label]] = [model.Label(text="Unnamed file")] |
15 | | - """The label of the file, defaults to 'Unnamed file'""" |
16 | | - path: Path |
17 | | - """The path to the file""" |
| 20 | + path: Optional[Path] = None |
| 21 | + """Where the content lives, as a Path. A field, not a property: a property |
| 22 | + of this name would win over the ``path`` field declared by |
| 23 | + ``osw.express.FileResult`` and make that field's value unreadable. Kept in |
| 24 | + step with the model's own ``local_path`` by the constructor, so either name |
| 25 | + reads the same file.""" |
18 | 26 |
|
19 | 27 | class Config: |
20 | 28 | arbitrary_types_allowed = True |
21 | 29 |
|
22 | | - @classmethod |
23 | | - def from_other( |
24 | | - cls, other: "FileController", path: Path, **kwargs |
25 | | - ) -> "LocalFileController": |
26 | | - return other.cast(cls, path=path, **kwargs) |
27 | | - |
28 | 30 | def __init__(self, **kwargs): |
| 31 | + path = kwargs.get("path") |
| 32 | + if path is not None and not kwargs.get("local_path"): |
| 33 | + kwargs["local_path"] = str(path) |
29 | 34 | super().__init__(**kwargs) |
| 35 | + if not self.local_path: |
| 36 | + # The controller this replaces declared path as a required field, |
| 37 | + # so an instance without one never existed. Raising here keeps the |
| 38 | + # error at the call site that is wrong, instead of surfacing it on |
| 39 | + # the first read. |
| 40 | + raise ValueError( |
| 41 | + f"{type(self).__name__} needs a file: pass path= or local_path=" |
| 42 | + ) |
| 43 | + self.path = Path(self.local_path) |
30 | 44 | self._set_metadata() |
31 | 45 |
|
| 46 | + @classmethod |
| 47 | + def from_other(cls, other, path=None, **kwargs) -> "LocalFileController": |
| 48 | + if path is not None: |
| 49 | + kwargs["local_path"] = str(path) |
| 50 | + return other.cast(cls, **kwargs) |
| 51 | + |
32 | 52 | def get(self) -> IO: |
33 | 53 | self._set_metadata() |
34 | | - return open(self.path, "rb") |
| 54 | + return super().get() |
35 | 55 |
|
36 | 56 | def put(self, file: IO, **kwargs: Dict[str, Any]): |
37 | 57 | self._set_metadata() |
38 | | - mode = "wb" |
39 | | - if isinstance(file, TextIOBase): |
40 | | - mode = "w" |
41 | | - with open(self.path, mode) as f: |
42 | | - # f.write(file.read()) # In-memory - limited by available RAM |
43 | | - shutil.copyfileobj(file, f) # Chunked copy |
44 | | - |
45 | | - def delete(self): |
46 | | - if os.path.exists(self.path): |
47 | | - os.remove(self.path) |
| 58 | + super().put(file, **kwargs) |
48 | 59 |
|
49 | 60 | def _set_metadata(self): |
50 | | - file = LocalFileController.extract_metadata(self.path) |
51 | | - self.label = file.label |
| 61 | + self.label = LocalFileController.extract_metadata(self.path).label |
52 | 62 |
|
53 | 63 | @classmethod |
54 | 64 | def extract_metadata(cls, path: Path) -> model.LocalFile: |
55 | | - file = model.LocalFile(label=[model.Label(text=path.name)]) |
| 65 | + file = model.LocalFile(label=[model.Label(text=Path(path).name)]) |
56 | 66 | if os.path.exists(path): |
57 | 67 | pass |
58 | 68 | # stats = os.stat(path) |
|
0 commit comments