pathseq 0.1.0__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.
pathseq-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.3
2
+ Name: pathseq
3
+ Version: 0.1.0
4
+ Summary: A pathlib-like library for working with file sequences.
5
+ Author: Ashley Whetter
6
+ Author-email: Ashley Whetter <ashley@awhetter.co.uk>
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Environment :: Plugins
9
+ Classifier: Framework :: Sphinx :: Extension
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Natural Language :: English
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Requires-Dist: lark
21
+ Requires-Dist: python-statemachine
22
+ Requires-Dist: typing-extensions
23
+ Requires-Python: >=3.10
24
+ Project-URL: Documentation, https://pathseq.readthedocs.org
25
+ Project-URL: Source, https://github.com/AWhetter/pathseq
26
+ Description-Content-Type: text/x-rst
27
+
28
+ pathseq
29
+ =======
30
+
31
+ .. image:: https://readthedocs.org/projects/pathseq/badge/?version=latest
32
+ :target: https://pathseq.readthedocs.org
33
+ :alt: Documentation
34
+
35
+ .. image:: https://github.com/AWhetter/pathseq/actions/workflows/main.yml/badge.svg?branch=main
36
+ :target: https://github.com/AWhetter/pathseq/actions/workflows/main.yml?query=branch%3Amain
37
+ :alt: Github Build Status
38
+
39
+ .. image:: https://img.shields.io/pypi/v/pathseq.svg
40
+ :target: https://pypi.org/project/pathseq/
41
+ :alt: PyPI Version
42
+
43
+ .. image:: https://img.shields.io/pypi/pyversions/pathseq.svg
44
+ :target: https://pypi.org/project/pathseq/
45
+ :alt: Supported Python Versions
46
+
47
+ A pathlib-first library for working with file sequences.
48
+
49
+ TODO: Note special features
50
+
51
+ * Multi-dimension ranges (eg animated udims)
52
+ * pathlib-first API
53
+ * Support for UDIM tokens
54
+ * Sequence compression?
55
+
56
+ Getting Started
57
+ ---------------
58
+
59
+ The following section demonstrates how to install ``pathseq`` and some basic usage.
60
+ Full documentation is available here: https://pathseq.readthedocs.io/en/latest/
61
+
62
+
63
+ Installation
64
+ ~~~~~~~~~~~~
65
+
66
+ ``pathseq`` can be installed through pip:
67
+
68
+ .. code-block:: bash
69
+
70
+ pip install pathseq
71
+
72
+ Usage
73
+ ~~~~~
74
+
75
+ TODO
76
+
77
+ For more detailed usage, see the documentation:
78
+ https://pathseq.readthedocs.io/en/latest/
79
+
80
+
81
+ Contributing
82
+ ------------
83
+
84
+ Running the tests
85
+ ~~~~~~~~~~~~~~~~~
86
+
87
+ Tests are executed through `tox <https://tox.readthedocs.io/en/latest/>`_.
88
+
89
+ .. code-block:: bash
90
+
91
+ tox
92
+
93
+
94
+ Code Style
95
+ ~~~~~~~~~~
96
+
97
+ Code is formatted using `black <https://github.com/python/black>`_.
98
+
99
+ You can check your formatting using black's check mode:
100
+
101
+ .. code-block:: bash
102
+
103
+ tox -e format
104
+
105
+ You can also get black to format your changes for you:
106
+
107
+ .. code-block:: bash
108
+
109
+ tox -e format -- src/ tests/
110
+
111
+
112
+ Release Notes
113
+ ~~~~~~~~~~~~~
114
+
115
+ Release notes are managed through `towncrier <https://towncrier.readthedocs.io/en/stable/index.html>`_.
116
+ When making a pull request you will need to create a news fragment to document your change:
117
+
118
+ .. code-block:: bash
119
+
120
+ tox -e release_notes -- create --help
121
+
122
+
123
+ Versioning
124
+ ----------
125
+
126
+ We use `SemVer <https://semver.org/>`_ for versioning.
127
+ For the versions available, see the `tags on this repository <https://github.com/AWhetter/pathseq/tags>`_.
128
+
129
+
130
+ License
131
+ -------
132
+
133
+ This project is licensed under the MIT License.
134
+ See the `LICENSE.rst <LICENSE.rst>`_ file for details.
@@ -0,0 +1,107 @@
1
+ pathseq
2
+ =======
3
+
4
+ .. image:: https://readthedocs.org/projects/pathseq/badge/?version=latest
5
+ :target: https://pathseq.readthedocs.org
6
+ :alt: Documentation
7
+
8
+ .. image:: https://github.com/AWhetter/pathseq/actions/workflows/main.yml/badge.svg?branch=main
9
+ :target: https://github.com/AWhetter/pathseq/actions/workflows/main.yml?query=branch%3Amain
10
+ :alt: Github Build Status
11
+
12
+ .. image:: https://img.shields.io/pypi/v/pathseq.svg
13
+ :target: https://pypi.org/project/pathseq/
14
+ :alt: PyPI Version
15
+
16
+ .. image:: https://img.shields.io/pypi/pyversions/pathseq.svg
17
+ :target: https://pypi.org/project/pathseq/
18
+ :alt: Supported Python Versions
19
+
20
+ A pathlib-first library for working with file sequences.
21
+
22
+ TODO: Note special features
23
+
24
+ * Multi-dimension ranges (eg animated udims)
25
+ * pathlib-first API
26
+ * Support for UDIM tokens
27
+ * Sequence compression?
28
+
29
+ Getting Started
30
+ ---------------
31
+
32
+ The following section demonstrates how to install ``pathseq`` and some basic usage.
33
+ Full documentation is available here: https://pathseq.readthedocs.io/en/latest/
34
+
35
+
36
+ Installation
37
+ ~~~~~~~~~~~~
38
+
39
+ ``pathseq`` can be installed through pip:
40
+
41
+ .. code-block:: bash
42
+
43
+ pip install pathseq
44
+
45
+ Usage
46
+ ~~~~~
47
+
48
+ TODO
49
+
50
+ For more detailed usage, see the documentation:
51
+ https://pathseq.readthedocs.io/en/latest/
52
+
53
+
54
+ Contributing
55
+ ------------
56
+
57
+ Running the tests
58
+ ~~~~~~~~~~~~~~~~~
59
+
60
+ Tests are executed through `tox <https://tox.readthedocs.io/en/latest/>`_.
61
+
62
+ .. code-block:: bash
63
+
64
+ tox
65
+
66
+
67
+ Code Style
68
+ ~~~~~~~~~~
69
+
70
+ Code is formatted using `black <https://github.com/python/black>`_.
71
+
72
+ You can check your formatting using black's check mode:
73
+
74
+ .. code-block:: bash
75
+
76
+ tox -e format
77
+
78
+ You can also get black to format your changes for you:
79
+
80
+ .. code-block:: bash
81
+
82
+ tox -e format -- src/ tests/
83
+
84
+
85
+ Release Notes
86
+ ~~~~~~~~~~~~~
87
+
88
+ Release notes are managed through `towncrier <https://towncrier.readthedocs.io/en/stable/index.html>`_.
89
+ When making a pull request you will need to create a news fragment to document your change:
90
+
91
+ .. code-block:: bash
92
+
93
+ tox -e release_notes -- create --help
94
+
95
+
96
+ Versioning
97
+ ----------
98
+
99
+ We use `SemVer <https://semver.org/>`_ for versioning.
100
+ For the versions available, see the `tags on this repository <https://github.com/AWhetter/pathseq/tags>`_.
101
+
102
+
103
+ License
104
+ -------
105
+
106
+ This project is licensed under the MIT License.
107
+ See the `LICENSE.rst <LICENSE.rst>`_ file for details.
@@ -0,0 +1,92 @@
1
+ [project]
2
+ name = "pathseq"
3
+ version = "0.1.0"
4
+ description = "A pathlib-like library for working with file sequences."
5
+ authors = [
6
+ {name = "Ashley Whetter", email = "ashley@awhetter.co.uk"},
7
+ ]
8
+ readme = "README.rst"
9
+ classifiers = [
10
+ "Development Status :: 4 - Beta",
11
+ "Environment :: Plugins",
12
+ "Framework :: Sphinx :: Extension",
13
+ "Intended Audience :: Developers",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Natural Language :: English",
16
+ "Programming Language :: Python",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
+ ]
24
+ requires-python = ">=3.10"
25
+ dependencies = [
26
+ "lark",
27
+ "python-statemachine",
28
+ "typing_extensions",
29
+ ]
30
+
31
+ [project.urls]
32
+ Source = "https://github.com/AWhetter/pathseq"
33
+ Documentation = "https://pathseq.readthedocs.org"
34
+
35
+ [dependency-groups]
36
+ dev = [
37
+ {include-group = "test"},
38
+ {include-group = "format"},
39
+ {include-group = "lint"},
40
+ {include-group = "typecheck"},
41
+ {include-group = "doc"},
42
+ ]
43
+ test = [
44
+ "fileseq",
45
+ "hypothesis",
46
+ "pytest",
47
+ ]
48
+ format = [
49
+ "ruff",
50
+ ]
51
+ lint = [
52
+ "ruff",
53
+ ]
54
+ typecheck = [
55
+ "mypy",
56
+ ]
57
+ doc = [
58
+ "furo",
59
+ "sphinx",
60
+ "sphinx-design",
61
+ ]
62
+ release_notes = [
63
+ "towncrier",
64
+ ]
65
+
66
+ [build-system]
67
+ requires = ["uv_build>=0.8.5,<0.9.0"]
68
+ build-backend = "uv_build"
69
+
70
+ [tool.uv.build-backend]
71
+ module-name = "pathseq"
72
+
73
+ [tool.pylint]
74
+ disable = "R,unused-argument"
75
+
76
+ [tool.towncrier]
77
+ directory = "doc/changes"
78
+ filename = "CHANGELOG.rst"
79
+ package = "pathseq"
80
+ title_format = "pathseq v{version} ({project_date})"
81
+
82
+ [tool.pytest.ini_options]
83
+ markers = [
84
+ "todo",
85
+ ]
86
+ addopts = "--doctest-glob='[!0]*.rst' --doctest-modules --ignore=doc/source/conf.py"
87
+ doctest_optionflags = "ELLIPSIS"
88
+ testpaths = [
89
+ "tests",
90
+ "doc/source",
91
+ "src",
92
+ ]
@@ -0,0 +1,44 @@
1
+ """A pathlib-first library for working with file sequences."""
2
+
3
+ from ._ast import (
4
+ FileNumT,
5
+ PaddedRange,
6
+ ParsedLooseSequence,
7
+ ParsedSequence,
8
+ Ranges,
9
+ RangesEndName,
10
+ RangesInName,
11
+ RangesStartName,
12
+ )
13
+ from ._base import BasePathSequence, BasePurePathSequence, PathT_co, PurePathT_co
14
+ from ._error import IncompleteDimensionError, NotASequenceError, ParseError
15
+ from ._file_num_seq import FileNumSequence
16
+ from ._loose_path_sequence import LoosePathSequence
17
+ from ._loose_pure_path_sequence import LoosePurePathSequence
18
+ from ._path_sequence import PathSequence
19
+ from ._pure_path_sequence import PurePathSequence
20
+
21
+ __version__ = "0.1.0"
22
+
23
+ __all__ = (
24
+ "BasePathSequence",
25
+ "BasePurePathSequence",
26
+ "FileNumSequence",
27
+ "FileNumT",
28
+ "IncompleteDimensionError",
29
+ "LoosePathSequence",
30
+ "LoosePurePathSequence",
31
+ "NotASequenceError",
32
+ "PaddedRange",
33
+ "ParsedLooseSequence",
34
+ "ParseError",
35
+ "ParsedSequence",
36
+ "PathSequence",
37
+ "PathT_co",
38
+ "PurePathSequence",
39
+ "PurePathT_co",
40
+ "Ranges",
41
+ "RangesEndName",
42
+ "RangesInName",
43
+ "RangesStartName",
44
+ )
@@ -0,0 +1,154 @@
1
+ from collections.abc import Iterator, Sequence
2
+ import decimal
3
+ from typing import overload, Protocol, TypeVar
4
+
5
+ from typing_extensions import Self # PY311
6
+
7
+ from ._ast import FileNumT
8
+ from ._decimal_range import DecimalRange
9
+
10
+
11
+ FileNumT_cov = TypeVar("FileNumT_cov", covariant=True)
12
+
13
+
14
+ class RangeProtocol(Protocol[FileNumT_cov]):
15
+ @property
16
+ def start(self) -> FileNumT_cov: ...
17
+ @property
18
+ def step(self) -> FileNumT_cov: ...
19
+
20
+ def __iter__(self) -> Iterator[FileNumT_cov]: ...
21
+
22
+ def __len__(self) -> int: ...
23
+
24
+
25
+ def remove_exponent(d: decimal.Decimal) -> decimal.Decimal:
26
+ """Remove the exponent and trailing zeros from the given decimal."""
27
+ return d.quantize(decimal.Decimal(1)) if d == d.to_integral() else d.normalize()
28
+
29
+
30
+ class ArithmeticSequence(Sequence[FileNumT]):
31
+ def __init__(
32
+ self, start: FileNumT, end: FileNumT | None = None, step: FileNumT | None = None
33
+ ) -> None:
34
+ end = end if end is not None else start
35
+ step = step if step is not None else start.__class__(1)
36
+
37
+ # Normalise to a positive step value
38
+ if step < 0:
39
+ start, end = (end, start)
40
+ step = -step
41
+
42
+ stop: FileNumT
43
+ if start < end:
44
+ # Normalise the end value to match the step
45
+ remainder = divmod(end - start, step)[1]
46
+ if remainder:
47
+ stop = end + (step - remainder)
48
+ end -= remainder
49
+ else:
50
+ stop = end + step
51
+ elif start == end:
52
+ # We treat the end as inclusive, ranges don't.
53
+ stop = end + step
54
+ # Normalise to a step of 1
55
+ step = start.__class__(1)
56
+ else:
57
+ stop = end
58
+
59
+ self._range: RangeProtocol[FileNumT]
60
+ self._end: FileNumT
61
+ if isinstance(start, int):
62
+ self._range = range(start, stop, step)
63
+ self._end = end
64
+ else:
65
+ self._range = DecimalRange(
66
+ remove_exponent(start), remove_exponent(stop), remove_exponent(step)
67
+ )
68
+ self._end = remove_exponent(end)
69
+
70
+ @property
71
+ def start(self) -> FileNumT:
72
+ return self._range.start
73
+
74
+ @property
75
+ def end(self) -> FileNumT:
76
+ return self._end
77
+
78
+ @property
79
+ def step(self) -> FileNumT:
80
+ return self._range.step
81
+
82
+ def __eq__(self, other: object) -> bool:
83
+ if not isinstance(other, ArithmeticSequence):
84
+ return NotImplemented
85
+
86
+ return (
87
+ isinstance(self.start, other.start.__class__)
88
+ and self.start == other.start
89
+ and self.end == other.end
90
+ and self.step == other.step
91
+ )
92
+
93
+ def __hash__(self) -> int:
94
+ return hash((type(self), self.start, self.end, self.step))
95
+
96
+ def __contains__(self, item: object) -> bool:
97
+ return any(item == x for x in self._range)
98
+
99
+ def __iter__(self) -> Iterator[FileNumT]:
100
+ return iter(self._range)
101
+
102
+ def __len__(self) -> int:
103
+ return len(self._range)
104
+
105
+ def __str__(self) -> str:
106
+ if len(self) == 1:
107
+ return str(self.start)
108
+
109
+ result = f"{self.start}-{self.end}"
110
+ if self.step != 1:
111
+ result += f"x{self.step}"
112
+ elif len(self) == 2:
113
+ return ",".join(str(x) for x in self)
114
+
115
+ return result
116
+
117
+ def __repr__(self) -> str:
118
+ if self.step == 1:
119
+ return f"{self.__class__.__name__}({self.start}, {self.end})"
120
+
121
+ return f"{self.__class__.__name__}({self.start}, {self.end}, {self.step})"
122
+
123
+ @overload
124
+ def __getitem__(self, index: int) -> FileNumT: ...
125
+
126
+ @overload
127
+ def __getitem__(self, index: slice) -> Self: ...
128
+
129
+ def __getitem__(self, index: int | slice) -> FileNumT | Self:
130
+ if isinstance(index, slice):
131
+ start_i, stop_i, step = index.indices(len(self))
132
+ if step > 0:
133
+ if start_i >= stop_i:
134
+ raise IndexError("slice creates an empty arithemtic sequence")
135
+ else:
136
+ if start_i <= stop_i:
137
+ raise IndexError("slice creates an empty arithemtic sequence")
138
+
139
+ start = self[index.start]
140
+ try:
141
+ stop = self[index.stop + 1]
142
+ except IndexError:
143
+ stop = self.end
144
+
145
+ return self.__class__(start, stop, self.step * index.step)
146
+
147
+ if index < 0:
148
+ index += len(self)
149
+ if index < 0:
150
+ raise IndexError("index out of range")
151
+ elif index >= len(self):
152
+ raise IndexError("index out of range")
153
+
154
+ return self._range.start + index * self._range.step
@@ -0,0 +1,22 @@
1
+ from ._base import FileNum, FileNumT, PaddedRange, Ranges
2
+ from ._formatter import Formatter
3
+ from ._loose_type import (
4
+ ParsedLooseSequence,
5
+ RangesStartName,
6
+ RangesInName,
7
+ RangesEndName,
8
+ )
9
+ from ._type import ParsedSequence
10
+
11
+ __all__ = [
12
+ "FileNum",
13
+ "FileNumT",
14
+ "Formatter",
15
+ "PaddedRange",
16
+ "ParsedLooseSequence",
17
+ "ParsedSequence",
18
+ "Ranges",
19
+ "RangesStartName",
20
+ "RangesInName",
21
+ "RangesEndName",
22
+ ]