wexample-file 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [year] [fullname]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: wexample-file
3
+ Version: 0.0.1
4
+ Summary: Package that allows you to manage the state of files and directories using YAML configuration files.
5
+ Author-email: weeger <contact@wexample.com>
6
+ License: MIT
7
+ Project-URL: homepage, https://github.com/wexample/python-file
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: pip-tools
15
+ Requires-Dist: pydantic
16
+ Requires-Dist: pytest
17
+ Requires-Dist: wexample-config==0.0.41
18
+ Requires-Dist: wexample-helpers==0.0.57
19
+ Requires-Dist: wexample-prompt==0.0.38
20
+ Dynamic: license-file
21
+
22
+ # wexample-file
23
+
24
+ Tools to manage the state of files and directories using simple YAML configuration files.
25
+
26
+ - Project: https://github.com/wexample/python-file
27
+ - License: MIT
28
+
29
+ Install:
30
+ ```bash
31
+ pip install wexample-file
32
+ ```
33
+
34
+ Quick start:
35
+ ```python
36
+ from wexample_file import *
37
+ # TODO: usage examples will go here
38
+ ```
@@ -0,0 +1,17 @@
1
+ # wexample-file
2
+
3
+ Tools to manage the state of files and directories using simple YAML configuration files.
4
+
5
+ - Project: https://github.com/wexample/python-file
6
+ - License: MIT
7
+
8
+ Install:
9
+ ```bash
10
+ pip install wexample-file
11
+ ```
12
+
13
+ Quick start:
14
+ ```python
15
+ from wexample_file import *
16
+ # TODO: usage examples will go here
17
+ ```
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools",
4
+ "wheel",
5
+ ]
6
+ build-backend = "setuptools.build_meta"
7
+
8
+ [project]
9
+ name = "wexample-file"
10
+ version = "0.0.1"
11
+ description = "Package that allows you to manage the state of files and directories using YAML configuration files."
12
+ authors = [
13
+ { name = "weeger", email = "contact@wexample.com" },
14
+ ]
15
+ requires-python = ">=3.6"
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ ]
21
+ dependencies = [
22
+ "pip-tools",
23
+ "pydantic",
24
+ "pytest",
25
+ "wexample-config==0.0.41",
26
+ "wexample-helpers==0.0.57",
27
+ "wexample-prompt==0.0.38",
28
+ ]
29
+
30
+ [project.readme]
31
+ file = "README.md"
32
+ content-type = "text/markdown"
33
+
34
+ [project.license]
35
+ text = "MIT"
36
+
37
+ [project.urls]
38
+ homepage = "https://github.com/wexample/python-file"
39
+
40
+ [tool.setuptools.packages.find]
41
+ include = [
42
+ "*",
43
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,76 @@
1
+ import pytest
2
+ from pathlib import Path
3
+
4
+ from wexample_file.common.local_directory import LocalDirectory
5
+ from wexample_file.excpetion.not_a_directory_exception import NotADirectoryException
6
+ from wexample_file.excpetion.directory_not_found_exception import DirectoryNotFoundException
7
+
8
+
9
+ def test_local_directory_instantiation_with_str(tmp_path):
10
+ d = tmp_path / "adir"
11
+ d.mkdir()
12
+ ld = LocalDirectory(path=str(d))
13
+ assert isinstance(ld.path, Path)
14
+ assert ld.path == d.resolve()
15
+
16
+
17
+ def test_local_directory_instantiation_with_path_nonexistent(tmp_path):
18
+ d = tmp_path / "missing_dir"
19
+ assert not d.exists()
20
+ ld = LocalDirectory(path=d)
21
+ assert ld.path == d.resolve()
22
+
23
+
24
+ def test_local_directory_rejects_file(tmp_path):
25
+ f = tmp_path / "afile.txt"
26
+ f.write_text("hello")
27
+ with pytest.raises(NotADirectoryException):
28
+ LocalDirectory(path=f)
29
+
30
+
31
+ def test_local_directory_check_exists_true_accepts_existing_dir(tmp_path):
32
+ d = tmp_path / "exists_dir"
33
+ d.mkdir()
34
+ ld = LocalDirectory(path=d, check_exists=True)
35
+ assert ld.path == d.resolve()
36
+
37
+
38
+ def test_local_directory_check_exists_true_rejects_missing(tmp_path):
39
+ d = tmp_path / "missing_dir2"
40
+ assert not d.exists()
41
+ with pytest.raises(DirectoryNotFoundException):
42
+ LocalDirectory(path=d, check_exists=True)
43
+
44
+
45
+ def test_local_directory_remove_deletes_directory_recursively(tmp_path):
46
+ d = tmp_path / "adir_to_remove"
47
+ sub = d / "sub"
48
+ sub.mkdir(parents=True)
49
+ (sub / "file.txt").write_text("hello")
50
+ ld = LocalDirectory(path=d, check_exists=True)
51
+ assert d.exists() and d.is_dir()
52
+ ld.remove()
53
+ assert not d.exists()
54
+
55
+
56
+ def test_local_directory_remove_idempotent(tmp_path):
57
+ d = tmp_path / "missing_dir_after_remove"
58
+ ld = LocalDirectory(path=d)
59
+ # First remove on non-existent path should not raise
60
+ ld.remove()
61
+ assert not d.exists()
62
+ # Create then remove, then remove again
63
+ d.mkdir()
64
+ ld2 = LocalDirectory(path=d, check_exists=True)
65
+ ld2.remove()
66
+ assert not d.exists()
67
+ # Idempotent second call
68
+ ld2.remove()
69
+
70
+
71
+ def test_local_directory_create_creates_directory_and_parents(tmp_path):
72
+ d = tmp_path / "a/b/c"
73
+ ld = LocalDirectory(path=d)
74
+ assert not d.exists()
75
+ ld.create()
76
+ assert d.exists() and d.is_dir()
@@ -0,0 +1,134 @@
1
+ import pytest
2
+ from pathlib import Path
3
+ from wexample_file.common.local_file import LocalFile
4
+ from wexample_file.excpetion.not_a_file_exception import NotAFileException
5
+ from wexample_file.excpetion.file_not_found_exception import FileNotFoundException
6
+
7
+
8
+ def test_local_file_instantiation_with_str(tmp_path):
9
+ p = tmp_path / "file.txt"
10
+ p.write_text("hello")
11
+ lf = LocalFile(path=str(p))
12
+ assert isinstance(lf.path, Path)
13
+ assert lf.path == p.resolve()
14
+
15
+
16
+ def test_local_file_instantiation_with_path_nonexistent(tmp_path):
17
+ p = tmp_path / "missing.txt"
18
+ assert not p.exists()
19
+ lf = LocalFile(path=p)
20
+ assert lf.path == p.resolve()
21
+
22
+
23
+ def test_local_file_rejects_directory(tmp_path):
24
+ d = tmp_path / "adir"
25
+ d.mkdir()
26
+ with pytest.raises(NotAFileException):
27
+ LocalFile(path=d)
28
+
29
+
30
+ def test_local_file_check_exists_true_accepts_existing_file(tmp_path):
31
+ p = tmp_path / "exists.txt"
32
+ p.write_text("data")
33
+ lf = LocalFile(path=p, check_exists=True)
34
+ assert lf.path == p.resolve()
35
+
36
+
37
+ def test_local_file_check_exists_true_rejects_missing(tmp_path):
38
+ p = tmp_path / "missing2.txt"
39
+ assert not p.exists()
40
+ with pytest.raises(FileNotFoundException):
41
+ LocalFile(path=p, check_exists=True)
42
+
43
+
44
+ def test_local_file_remove_deletes_file(tmp_path):
45
+ p = tmp_path / "toremove.txt"
46
+ p.write_text("data")
47
+ lf = LocalFile(path=p, check_exists=True)
48
+ assert p.exists()
49
+ lf.remove()
50
+ assert not p.exists()
51
+
52
+
53
+ def test_local_file_remove_idempotent(tmp_path):
54
+ p = tmp_path / "missing_after_remove.txt"
55
+ lf = LocalFile(path=p)
56
+ # First remove on non-existent path should not raise
57
+ lf.remove()
58
+ assert not p.exists()
59
+ # Create then remove, then remove again
60
+ p.write_text("hello")
61
+ lf2 = LocalFile(path=p, check_exists=True)
62
+ lf2.remove()
63
+ assert not p.exists()
64
+ # Idempotent second call
65
+ lf2.remove()
66
+
67
+
68
+ def test_local_file_read_returns_content_when_exists(tmp_path):
69
+ p = tmp_path / "readme.txt"
70
+ content = "héllo world"
71
+ p.write_text(content, encoding="utf-8")
72
+ lf = LocalFile(path=p)
73
+ assert lf.read() == content
74
+
75
+
76
+ def test_local_file_read_returns_none_when_missing(tmp_path):
77
+ p = tmp_path / "missing_read.txt"
78
+ lf = LocalFile(path=p)
79
+ assert lf.read() is None
80
+
81
+
82
+ def test_local_file_read_raises_if_not_a_file(tmp_path):
83
+ d = tmp_path / "not_a_file"
84
+ d.mkdir()
85
+ # Constructing LocalFile with a directory path would already raise NotAFileException
86
+ # So we simulate a race: create a file path then replace with directory
87
+ p = tmp_path / "was_file.txt"
88
+ p.write_text("x")
89
+ lf = LocalFile(path=p)
90
+ # Replace the path with a directory at same location
91
+ p.unlink()
92
+ d.rename(p)
93
+
94
+ assert lf.read() is None
95
+
96
+
97
+ def test_local_file_touch_creates_file_and_parents(tmp_path):
98
+ nested = tmp_path / "a/b/c/file.txt"
99
+ lf = LocalFile(path=nested)
100
+ assert not nested.exists()
101
+ lf.touch()
102
+ assert nested.exists() and nested.is_file()
103
+
104
+
105
+ def test_local_file_write_writes_content_and_creates_parents(tmp_path):
106
+ nested = tmp_path / "x/y/z/out.txt"
107
+ lf = LocalFile(path=nested)
108
+ text = "some content"
109
+ lf.write(text)
110
+ assert nested.exists() and nested.read_text() == text
111
+
112
+
113
+ def test_local_file_get_extension_simple(tmp_path):
114
+ p = tmp_path / "report.pdf"
115
+ lf = LocalFile(path=p)
116
+ assert lf.get_extension() == "pdf"
117
+
118
+
119
+ def test_local_file_get_extension_compound(tmp_path):
120
+ p = tmp_path / "archive.tar.gz"
121
+ lf = LocalFile(path=p)
122
+ assert lf.get_extension() == "gz"
123
+
124
+
125
+ def test_local_file_get_extension_none(tmp_path):
126
+ p = tmp_path / "README"
127
+ lf = LocalFile(path=p)
128
+ assert lf.get_extension() == ""
129
+
130
+
131
+ def test_local_file_get_extension_hidden_file(tmp_path):
132
+ p = tmp_path / ".gitignore"
133
+ lf = LocalFile(path=p)
134
+ assert lf.get_extension() == ""
File without changes
@@ -0,0 +1,85 @@
1
+ from abc import ABC, abstractmethod
2
+ from pathlib import Path
3
+
4
+ from pydantic import BaseModel, Field, field_validator, model_validator
5
+ from wexample_file.excpetion.local_path_not_found_exception import LocalPathNotFoundException
6
+
7
+
8
+ class AbstractLocalItemPath(BaseModel, ABC):
9
+ """Abstract base class for handling local file system paths.
10
+
11
+ Accepts either a string or a pathlib.Path for ``path`` and always stores a
12
+ resolved absolute Path (with user home expanded). This keeps comparisons and
13
+ downstream usage consistent regardless of how the input was provided.
14
+ """
15
+ path: Path = Field(description="The path to the file or directory")
16
+ check_exists: bool = False
17
+
18
+ @field_validator("path", mode="before")
19
+ @classmethod
20
+ def _coerce_and_resolve_path(cls, v):
21
+ """Coerce input into a resolved Path.
22
+
23
+ - Accepts str or Path
24
+ - Expands '~' and resolves to an absolute path with strict=False
25
+ """
26
+ if isinstance(v, str):
27
+ v = Path(v)
28
+ if isinstance(v, Path):
29
+ return v.expanduser().resolve(strict=False)
30
+ raise TypeError("path must be a str or pathlib.Path")
31
+
32
+ @model_validator(mode="after")
33
+ def _validate_existence(self):
34
+ """If check_exists is True, ensure the path exists."""
35
+ if self.check_exists and not self.path.exists():
36
+ # Defer to subclass to choose the most specific exception
37
+ exc = self._not_found_exc()
38
+ if exc is None:
39
+ # Fallback to a generic not-found exception
40
+ raise LocalPathNotFoundException(self.path)
41
+ raise exc
42
+ return self
43
+
44
+ @abstractmethod
45
+ def _kind(self) -> str:
46
+ """Return the kind of local item (e.g., 'file' or 'directory').
47
+
48
+ Subclasses must implement this to mark the class as abstract and to
49
+ provide a simple discriminator for debugging and representation.
50
+ """
51
+
52
+ @abstractmethod
53
+ def _not_found_exc(self) -> Exception | None:
54
+ """Return a specific 'not found' exception instance for this item type.
55
+
56
+ Subclasses should return an instance of a custom exception that best
57
+ represents the missing path for their type (e.g., FileNotFoundException
58
+ or DirectoryNotFoundException). Returning None will make the base class
59
+ fall back to LocalPathNotFoundException.
60
+ """
61
+
62
+ @abstractmethod
63
+ def remove(self) -> None:
64
+ """Remove the underlying path from the filesystem.
65
+
66
+ - For a file implementation, this should delete the file.
67
+ - For a directory implementation, this should delete the directory
68
+ recursively.
69
+ - This operation should be idempotent: if the path does not exist,
70
+ the method should complete without raising.
71
+ """
72
+
73
+ def __str__(self) -> str:
74
+ return str(self.path)
75
+
76
+ def __repr__(self) -> str:
77
+ return f"{self.__class__.__name__}(path={repr(str(self.path))}, check_exists={self.check_exists})"
78
+
79
+ def __eq__(self, other) -> bool:
80
+ if isinstance(other, AbstractLocalItemPath):
81
+ return self.path == other.path
82
+ if isinstance(other, (str, Path)):
83
+ other_path = Path(other).expanduser().resolve(strict=False)
84
+ return self.path == other_path
85
+ return NotImplemented
@@ -0,0 +1,54 @@
1
+ from pathlib import Path
2
+
3
+ from pydantic import field_validator
4
+
5
+ from wexample_file.excpetion.directory_not_found_exception import DirectoryNotFoundException
6
+ from wexample_file.excpetion.not_a_directory_exception import NotADirectoryException
7
+ from .abstract_local_item_path import AbstractLocalItemPath
8
+
9
+
10
+ class LocalDirectory(AbstractLocalItemPath):
11
+ """Represents a local directory path.
12
+
13
+ The path is stored as a resolved absolute Path. If the path exists, it must
14
+ be a directory.
15
+ """
16
+
17
+ @field_validator("path")
18
+ @classmethod
19
+ def _validate_is_dir(cls, v: Path) -> Path:
20
+ if v.exists() and not v.is_dir():
21
+ raise NotADirectoryException(v)
22
+ return v
23
+
24
+ def _kind(self) -> str:
25
+ from wexample_file.const.globals import PATH_NAME_DIRECTORY
26
+
27
+ return PATH_NAME_DIRECTORY
28
+
29
+ def _not_found_exc(self):
30
+ return DirectoryNotFoundException(self.path)
31
+
32
+ def remove(self) -> None:
33
+ """Delete the directory recursively if it exists; no-op if it doesn't.
34
+
35
+ This method is idempotent and will not raise if the directory is missing.
36
+ """
37
+ if not self.path.exists():
38
+ return
39
+ if self.path.is_dir():
40
+ # Remove contents recursively
41
+ import shutil
42
+ shutil.rmtree(self.path)
43
+ else:
44
+ # If for some reason it's not a dir anymore, best-effort unlink
45
+ try:
46
+ self.path.unlink()
47
+ except FileNotFoundError:
48
+ pass
49
+
50
+ def create(self, parents: bool = True, exist_ok: bool = True) -> None:
51
+ if self.path.exists() and self.path.is_file():
52
+ return None
53
+
54
+ self.path.mkdir(parents=parents, exist_ok=exist_ok)
@@ -0,0 +1,95 @@
1
+ from pathlib import Path
2
+
3
+ from pydantic import field_validator
4
+
5
+ from wexample_file.excpetion.file_not_found_exception import FileNotFoundException
6
+ from wexample_file.excpetion.not_a_file_exception import NotAFileException
7
+ from .abstract_local_item_path import AbstractLocalItemPath
8
+
9
+
10
+ class LocalFile(AbstractLocalItemPath):
11
+ """Represents a local file path.
12
+
13
+ The path is stored as a resolved absolute Path. If the path exists, it must
14
+ be a file.
15
+ """
16
+
17
+ @field_validator("path")
18
+ @classmethod
19
+ def _validate_is_file(cls, v: Path) -> Path:
20
+ # Only validate type when it exists; creation workflows may pass a non-existent path
21
+ if v.exists() and not v.is_file():
22
+ raise NotAFileException(v)
23
+ return v
24
+
25
+ def _kind(self) -> str:
26
+ from wexample_file.const.globals import PATH_NAME_FILE
27
+
28
+ return PATH_NAME_FILE
29
+
30
+ def _not_found_exc(self):
31
+ return FileNotFoundException(self.path)
32
+
33
+ def remove(self) -> None:
34
+ """Delete the file if it exists; no-op if it doesn't.
35
+
36
+ This method is idempotent and will not raise if the file is missing.
37
+ """
38
+ try:
39
+ # unlink(missing_ok=True) is available in Python 3.8+
40
+ self.path.unlink(missing_ok=True)
41
+ except TypeError:
42
+ # Fallback for older Python: check existence first
43
+ if self.path.exists():
44
+ self.path.unlink()
45
+
46
+ def read(self, encoding: str = "utf-8") -> str | None:
47
+ """Read and return the file content as text, or None if it doesn't exist.
48
+
49
+ Parameters:
50
+ encoding: Text encoding used to decode file content. Defaults to 'utf-8'.
51
+ """
52
+ if not self.path.exists() or not self.path.is_file():
53
+ return None
54
+
55
+ return self.path.read_text(encoding=encoding)
56
+
57
+ def touch(self, parents: bool = True, exist_ok: bool = True) -> bool:
58
+ if parents:
59
+ self.path.parent.mkdir(parents=True, exist_ok=True)
60
+ if self.path.exists() or self.path.is_dir():
61
+ return False
62
+
63
+ self.path.touch(exist_ok=exist_ok)
64
+ return True
65
+
66
+ def write(self, content: str, encoding: str = "utf-8", make_parents: bool = True) -> None:
67
+ """Write text content to the file, creating it if necessary.
68
+ """
69
+ if make_parents:
70
+ self.path.parent.mkdir(parents=True, exist_ok=True)
71
+ if self.path.exists() and self.path.is_dir():
72
+ raise NotAFileException(self.path)
73
+ self.path.write_text(content, encoding=encoding)
74
+
75
+ def get_extension(self) -> str:
76
+ """Return the last suffix without the leading dot.
77
+
78
+ Examples:
79
+ "archive.tar.gz" -> "gz"
80
+ "report.pdf" -> "pdf"
81
+ "README" -> ""
82
+ """
83
+ suf = self.path.suffix
84
+ return suf[1:] if suf.startswith(".") else ""
85
+
86
+ def change_extension(self, new_extension: str) -> None:
87
+ # Normalize extension: allow callers to pass with or without dot
88
+ ext = new_extension.lstrip(".")
89
+ suffix = f".{ext}" if ext else ""
90
+ target = self.path.with_suffix(suffix)
91
+
92
+ self.path.replace(target)
93
+
94
+ def is_empty(self) -> bool:
95
+ return Path(self.path).stat().st_size == 0
@@ -0,0 +1,2 @@
1
+ PATH_NAME_FILE = 'file'
2
+ PATH_NAME_DIRECTORY = 'directory'
@@ -0,0 +1,4 @@
1
+ from pathlib import Path
2
+ from typing import Union
3
+
4
+ PathOrString = Union[Path, str]
@@ -0,0 +1,9 @@
1
+ from wexample_file.excpetion.local_path_not_found_exception import LocalPathNotFoundException
2
+
3
+
4
+ class DirectoryNotFoundException(LocalPathNotFoundException):
5
+ error_code: str = "DIRECTORY_NOT_FOUND"
6
+
7
+ def __init__(self, path, message: str | None = None):
8
+ msg = message or f"Directory does not exist: {path}"
9
+ super().__init__(path=path, message=msg)
@@ -0,0 +1,9 @@
1
+ from wexample_file.excpetion.local_path_not_found_exception import LocalPathNotFoundException
2
+
3
+
4
+ class FileNotFoundException(LocalPathNotFoundException):
5
+ error_code: str = "FILE_NOT_FOUND"
6
+
7
+ def __init__(self, path, message: str | None = None):
8
+ msg = message or f"File does not exist: {path}"
9
+ super().__init__(path=path, message=msg)
@@ -0,0 +1,9 @@
1
+ from wexample_helpers.exception.undefined_exception import UndefinedException
2
+
3
+
4
+ class LocalPathNotFoundException(UndefinedException):
5
+ error_code: str = "LOCAL_PATH_NOT_FOUND"
6
+
7
+ def __init__(self, path, message: str | None = None):
8
+ msg = message or f"Path does not exist: {path}"
9
+ super().__init__(msg, data={"path": str(path)})
@@ -0,0 +1,9 @@
1
+ from wexample_helpers.exception.undefined_exception import UndefinedException
2
+
3
+
4
+ class NotADirectoryException(UndefinedException):
5
+ error_code: str = "DIRECTORY_EXPECTED"
6
+
7
+ def __init__(self, path, message: str | None = None):
8
+ msg = message or f"Path is not a directory: {path}"
9
+ super().__init__(msg, data={"path": str(path)})
@@ -0,0 +1,9 @@
1
+ from wexample_helpers.exception.undefined_exception import UndefinedException
2
+
3
+
4
+ class NotAFileException(UndefinedException):
5
+ error_code: str = "FILE_EXPECTED"
6
+
7
+ def __init__(self, path, message: str | None = None):
8
+ msg = message or f"Path is not a file: {path}"
9
+ super().__init__(msg, data={"path": str(path)})
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: wexample-file
3
+ Version: 0.0.1
4
+ Summary: Package that allows you to manage the state of files and directories using YAML configuration files.
5
+ Author-email: weeger <contact@wexample.com>
6
+ License: MIT
7
+ Project-URL: homepage, https://github.com/wexample/python-file
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: pip-tools
15
+ Requires-Dist: pydantic
16
+ Requires-Dist: pytest
17
+ Requires-Dist: wexample-config==0.0.41
18
+ Requires-Dist: wexample-helpers==0.0.57
19
+ Requires-Dist: wexample-prompt==0.0.38
20
+ Dynamic: license-file
21
+
22
+ # wexample-file
23
+
24
+ Tools to manage the state of files and directories using simple YAML configuration files.
25
+
26
+ - Project: https://github.com/wexample/python-file
27
+ - License: MIT
28
+
29
+ Install:
30
+ ```bash
31
+ pip install wexample-file
32
+ ```
33
+
34
+ Quick start:
35
+ ```python
36
+ from wexample_file import *
37
+ # TODO: usage examples will go here
38
+ ```
@@ -0,0 +1,22 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ tests/__init__.py
5
+ tests/package/common/test_local_directory.py
6
+ tests/package/common/test_local_file.py
7
+ wexample_file/__init__.py
8
+ wexample_file.egg-info/PKG-INFO
9
+ wexample_file.egg-info/SOURCES.txt
10
+ wexample_file.egg-info/dependency_links.txt
11
+ wexample_file.egg-info/requires.txt
12
+ wexample_file.egg-info/top_level.txt
13
+ wexample_file/common/abstract_local_item_path.py
14
+ wexample_file/common/local_directory.py
15
+ wexample_file/common/local_file.py
16
+ wexample_file/const/globals.py
17
+ wexample_file/const/types.py
18
+ wexample_file/excpetion/directory_not_found_exception.py
19
+ wexample_file/excpetion/file_not_found_exception.py
20
+ wexample_file/excpetion/local_path_not_found_exception.py
21
+ wexample_file/excpetion/not_a_directory_exception.py
22
+ wexample_file/excpetion/not_a_file_exception.py
@@ -0,0 +1,6 @@
1
+ pip-tools
2
+ pydantic
3
+ pytest
4
+ wexample-config==0.0.41
5
+ wexample-helpers==0.0.57
6
+ wexample-prompt==0.0.38
@@ -0,0 +1,4 @@
1
+ build
2
+ dist
3
+ tests
4
+ wexample_file