pathseq 0.1.0__tar.gz → 1.0.0.post0__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 → pathseq-1.0.0.post0}/PKG-INFO +82 -14
- pathseq-1.0.0.post0/README.rst +175 -0
- pathseq-1.0.0.post0/pyproject.toml +96 -0
- pathseq-0.1.0/pyproject.toml → pathseq-1.0.0.post0/pyproject.toml.orig +8 -7
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/__init__.py +8 -6
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_ast/__init__.py +7 -7
- pathseq-1.0.0.post0/src/pathseq/_ast/_formatter.py +138 -0
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_ast/_loose_type.py +24 -26
- pathseq-1.0.0.post0/src/pathseq/_ast/_ranges.py +72 -0
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_ast/_type.py +9 -10
- pathseq-1.0.0.post0/src/pathseq/_ast/_util.py +98 -0
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_base.py +61 -26
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_error.py +0 -24
- pathseq-1.0.0.post0/src/pathseq/_file_num_seq/__init__.py +7 -0
- {pathseq-0.1.0/src/pathseq → pathseq-1.0.0.post0/src/pathseq/_file_num_seq}/_arithmetic_sequence.py +8 -9
- {pathseq-0.1.0/src/pathseq → pathseq-1.0.0.post0/src/pathseq/_file_num_seq}/_decimal_range.py +14 -12
- {pathseq-0.1.0/src/pathseq → pathseq-1.0.0.post0/src/pathseq/_file_num_seq}/_file_num_seq.py +8 -7
- {pathseq-0.1.0/src/pathseq → pathseq-1.0.0.post0/src/pathseq/_file_num_seq}/_parse_file_num_seq.py +6 -6
- pathseq-0.1.0/src/pathseq/_formatter.py → pathseq-1.0.0.post0/src/pathseq/_formatters.py +3 -3
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_from_disk.py +62 -25
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_loose_pure_path_sequence.py +10 -35
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_parse_loose_path_sequence.py +124 -77
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_parse_path_sequence.py +25 -22
- pathseq-1.0.0.post0/src/pathseq/_pure_path_sequence.py +25 -0
- pathseq-0.1.0/README.rst +0 -107
- pathseq-0.1.0/src/pathseq/_ast/_base.py +0 -236
- pathseq-0.1.0/src/pathseq/_ast/_formatter.py +0 -38
- pathseq-0.1.0/src/pathseq/_pure_path_sequence.py +0 -58
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_loose_path_sequence.py +0 -0
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_path_sequence.py +0 -0
- {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pathseq
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0.post0
|
|
4
4
|
Summary: A pathlib-like library for working with file sequences.
|
|
5
5
|
Author: Ashley Whetter
|
|
6
6
|
Author-email: Ashley Whetter <ashley@awhetter.co.uk>
|
|
@@ -21,8 +21,8 @@ Requires-Dist: lark
|
|
|
21
21
|
Requires-Dist: python-statemachine
|
|
22
22
|
Requires-Dist: typing-extensions
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
|
-
Project-URL: Documentation, https://pathseq.readthedocs.org
|
|
25
24
|
Project-URL: Source, https://github.com/AWhetter/pathseq
|
|
25
|
+
Project-URL: Documentation, https://pathseq.readthedocs.org
|
|
26
26
|
Description-Content-Type: text/x-rst
|
|
27
27
|
|
|
28
28
|
pathseq
|
|
@@ -44,14 +44,12 @@ pathseq
|
|
|
44
44
|
:target: https://pypi.org/project/pathseq/
|
|
45
45
|
:alt: Supported Python Versions
|
|
46
46
|
|
|
47
|
-
A pathlib-first library for working with
|
|
47
|
+
A pathlib-first library for working with path sequences.
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
* Multi-dimension ranges (eg animated udims)
|
|
49
|
+
* Multi-dimension ranges (e.g. animated udims)
|
|
52
50
|
* pathlib-first API
|
|
53
51
|
* Support for UDIM tokens
|
|
54
|
-
|
|
52
|
+
|
|
55
53
|
|
|
56
54
|
Getting Started
|
|
57
55
|
---------------
|
|
@@ -63,20 +61,90 @@ Full documentation is available here: https://pathseq.readthedocs.io/en/latest/
|
|
|
63
61
|
Installation
|
|
64
62
|
~~~~~~~~~~~~
|
|
65
63
|
|
|
66
|
-
``pathseq`` can be installed
|
|
64
|
+
``pathseq`` can be installed from PyPI:
|
|
67
65
|
|
|
68
66
|
.. code-block:: bash
|
|
69
67
|
|
|
70
68
|
pip install pathseq
|
|
71
69
|
|
|
70
|
+
|
|
72
71
|
Usage
|
|
73
72
|
~~~~~
|
|
74
73
|
|
|
75
|
-
TODO
|
|
76
|
-
|
|
77
74
|
For more detailed usage, see the documentation:
|
|
78
75
|
https://pathseq.readthedocs.io/en/latest/
|
|
79
76
|
|
|
77
|
+
Now, let's get started:
|
|
78
|
+
|
|
79
|
+
.. code-block:: pycon
|
|
80
|
+
|
|
81
|
+
>>> from pathseq import PathSequence
|
|
82
|
+
>>> seq = PathSequence("tests/fixtures/simple/images.1-5####.exr")
|
|
83
|
+
>>> for path in seq:
|
|
84
|
+
... path
|
|
85
|
+
...
|
|
86
|
+
PosixPath('tests/fixtures/simple/images.0001.exr')
|
|
87
|
+
PosixPath('tests/fixtures/simple/images.0002.exr')
|
|
88
|
+
PosixPath('tests/fixtures/simple/images.0003.exr')
|
|
89
|
+
PosixPath('tests/fixtures/simple/images.0004.exr')
|
|
90
|
+
PosixPath('tests/fixtures/simple/images.0005.exr')
|
|
91
|
+
|
|
92
|
+
>>> seq = PathSequence("tests/fixtures/simple/images.1-5####.exr")
|
|
93
|
+
>>> for path in seq:
|
|
94
|
+
... path.touch(exist_ok=True)
|
|
95
|
+
...
|
|
96
|
+
>>> seq2 = PathSequence("tests/fixtures/simple/images.####.exr")
|
|
97
|
+
>>> for path in seq2.iter_existing_paths():
|
|
98
|
+
... path
|
|
99
|
+
...
|
|
100
|
+
PosixPath('tests/fixtures/simple/images.0001.exr')
|
|
101
|
+
PosixPath('tests/fixtures/simple/images.0002.exr')
|
|
102
|
+
PosixPath('tests/fixtures/simple/images.0003.exr')
|
|
103
|
+
PosixPath('tests/fixtures/simple/images.0004.exr')
|
|
104
|
+
PosixPath('tests/fixtures/simple/images.0005.exr')
|
|
105
|
+
|
|
106
|
+
>>> seq = PathSequence("tests/fixtures/simple/images.1-5####.exr")
|
|
107
|
+
>>> seq.parent
|
|
108
|
+
PosixPath('tests/fixtures/simple')
|
|
109
|
+
>>> seq.parent / PathSequence("images.1-5####.exr")
|
|
110
|
+
... PathSequence("tests/fixtures/simple/images.1-5####.exr")
|
|
111
|
+
|
|
112
|
+
>>> anim_udims = PathSequence("/path/to/textures.1011-1012<UDIM>_1-3#.tex")
|
|
113
|
+
>>> for path in anim_udims:
|
|
114
|
+
... path
|
|
115
|
+
...
|
|
116
|
+
PosixPath('/path/to/textures.1011_1.tex')
|
|
117
|
+
PosixPath('/path/to/textures.1011_2.tex')
|
|
118
|
+
PosixPath('/path/to/textures.1011_3.tex')
|
|
119
|
+
PosixPath('/path/to/textures.1012_1.tex')
|
|
120
|
+
PosixPath('/path/to/textures.1012_2.tex')
|
|
121
|
+
PosixPath('/path/to/textures.1012_3.tex')
|
|
122
|
+
|
|
123
|
+
A wide range of sequence string formats are supported:
|
|
124
|
+
|
|
125
|
+
.. code-block:: pycon
|
|
126
|
+
|
|
127
|
+
>>> from pathseq import LoosePathSequence
|
|
128
|
+
>>> seq = LoosePathSequence("/path/to/images.1-5####.exr")
|
|
129
|
+
|
|
130
|
+
>>> seq = LoosePathSequence("/path/to/1-5####_images.exr")
|
|
131
|
+
>>> seq[0]
|
|
132
|
+
PosixPath('/path/to/0001_images.exr')
|
|
133
|
+
>>> seq.suffixes
|
|
134
|
+
('.exr',)
|
|
135
|
+
|
|
136
|
+
>>> LoosePathSequence("/path/to/1-5####_archives.tar.gz").suffixes
|
|
137
|
+
('.tar', '.gz')
|
|
138
|
+
|
|
139
|
+
>>> seq = LoosePathSequence("/path/to/images.exr.1-5####")
|
|
140
|
+
>>> seq[0]
|
|
141
|
+
PosixPath('/path/to/images.exr.0001')
|
|
142
|
+
>>> seq.suffixes
|
|
143
|
+
('.exr',)
|
|
144
|
+
|
|
145
|
+
>>> LoosePathSequence("/path/to/images.1001-1005<UDIM>.exr")[0]
|
|
146
|
+
PosixPath('/path/to/images.1001.exr')
|
|
147
|
+
|
|
80
148
|
|
|
81
149
|
Contributing
|
|
82
150
|
------------
|
|
@@ -94,19 +162,19 @@ Tests are executed through `tox <https://tox.readthedocs.io/en/latest/>`_.
|
|
|
94
162
|
Code Style
|
|
95
163
|
~~~~~~~~~~
|
|
96
164
|
|
|
97
|
-
Code is formatted using `
|
|
165
|
+
Code is formatted using `ruff <https://docs.astral.sh/ruff/formatter/>`_.
|
|
98
166
|
|
|
99
|
-
You can check your formatting using
|
|
167
|
+
You can check your formatting using ruff's check mode:
|
|
100
168
|
|
|
101
169
|
.. code-block:: bash
|
|
102
170
|
|
|
103
171
|
tox -e format
|
|
104
172
|
|
|
105
|
-
You can also get
|
|
173
|
+
You can also get ruff to format your changes for you:
|
|
106
174
|
|
|
107
175
|
.. code-block:: bash
|
|
108
176
|
|
|
109
|
-
tox -e
|
|
177
|
+
tox -e autoformat
|
|
110
178
|
|
|
111
179
|
|
|
112
180
|
Release Notes
|
|
@@ -0,0 +1,175 @@
|
|
|
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 path sequences.
|
|
21
|
+
|
|
22
|
+
* Multi-dimension ranges (e.g. animated udims)
|
|
23
|
+
* pathlib-first API
|
|
24
|
+
* Support for UDIM tokens
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
Getting Started
|
|
28
|
+
---------------
|
|
29
|
+
|
|
30
|
+
The following section demonstrates how to install ``pathseq`` and some basic usage.
|
|
31
|
+
Full documentation is available here: https://pathseq.readthedocs.io/en/latest/
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
Installation
|
|
35
|
+
~~~~~~~~~~~~
|
|
36
|
+
|
|
37
|
+
``pathseq`` can be installed from PyPI:
|
|
38
|
+
|
|
39
|
+
.. code-block:: bash
|
|
40
|
+
|
|
41
|
+
pip install pathseq
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
Usage
|
|
45
|
+
~~~~~
|
|
46
|
+
|
|
47
|
+
For more detailed usage, see the documentation:
|
|
48
|
+
https://pathseq.readthedocs.io/en/latest/
|
|
49
|
+
|
|
50
|
+
Now, let's get started:
|
|
51
|
+
|
|
52
|
+
.. code-block:: pycon
|
|
53
|
+
|
|
54
|
+
>>> from pathseq import PathSequence
|
|
55
|
+
>>> seq = PathSequence("tests/fixtures/simple/images.1-5####.exr")
|
|
56
|
+
>>> for path in seq:
|
|
57
|
+
... path
|
|
58
|
+
...
|
|
59
|
+
PosixPath('tests/fixtures/simple/images.0001.exr')
|
|
60
|
+
PosixPath('tests/fixtures/simple/images.0002.exr')
|
|
61
|
+
PosixPath('tests/fixtures/simple/images.0003.exr')
|
|
62
|
+
PosixPath('tests/fixtures/simple/images.0004.exr')
|
|
63
|
+
PosixPath('tests/fixtures/simple/images.0005.exr')
|
|
64
|
+
|
|
65
|
+
>>> seq = PathSequence("tests/fixtures/simple/images.1-5####.exr")
|
|
66
|
+
>>> for path in seq:
|
|
67
|
+
... path.touch(exist_ok=True)
|
|
68
|
+
...
|
|
69
|
+
>>> seq2 = PathSequence("tests/fixtures/simple/images.####.exr")
|
|
70
|
+
>>> for path in seq2.iter_existing_paths():
|
|
71
|
+
... path
|
|
72
|
+
...
|
|
73
|
+
PosixPath('tests/fixtures/simple/images.0001.exr')
|
|
74
|
+
PosixPath('tests/fixtures/simple/images.0002.exr')
|
|
75
|
+
PosixPath('tests/fixtures/simple/images.0003.exr')
|
|
76
|
+
PosixPath('tests/fixtures/simple/images.0004.exr')
|
|
77
|
+
PosixPath('tests/fixtures/simple/images.0005.exr')
|
|
78
|
+
|
|
79
|
+
>>> seq = PathSequence("tests/fixtures/simple/images.1-5####.exr")
|
|
80
|
+
>>> seq.parent
|
|
81
|
+
PosixPath('tests/fixtures/simple')
|
|
82
|
+
>>> seq.parent / PathSequence("images.1-5####.exr")
|
|
83
|
+
... PathSequence("tests/fixtures/simple/images.1-5####.exr")
|
|
84
|
+
|
|
85
|
+
>>> anim_udims = PathSequence("/path/to/textures.1011-1012<UDIM>_1-3#.tex")
|
|
86
|
+
>>> for path in anim_udims:
|
|
87
|
+
... path
|
|
88
|
+
...
|
|
89
|
+
PosixPath('/path/to/textures.1011_1.tex')
|
|
90
|
+
PosixPath('/path/to/textures.1011_2.tex')
|
|
91
|
+
PosixPath('/path/to/textures.1011_3.tex')
|
|
92
|
+
PosixPath('/path/to/textures.1012_1.tex')
|
|
93
|
+
PosixPath('/path/to/textures.1012_2.tex')
|
|
94
|
+
PosixPath('/path/to/textures.1012_3.tex')
|
|
95
|
+
|
|
96
|
+
A wide range of sequence string formats are supported:
|
|
97
|
+
|
|
98
|
+
.. code-block:: pycon
|
|
99
|
+
|
|
100
|
+
>>> from pathseq import LoosePathSequence
|
|
101
|
+
>>> seq = LoosePathSequence("/path/to/images.1-5####.exr")
|
|
102
|
+
|
|
103
|
+
>>> seq = LoosePathSequence("/path/to/1-5####_images.exr")
|
|
104
|
+
>>> seq[0]
|
|
105
|
+
PosixPath('/path/to/0001_images.exr')
|
|
106
|
+
>>> seq.suffixes
|
|
107
|
+
('.exr',)
|
|
108
|
+
|
|
109
|
+
>>> LoosePathSequence("/path/to/1-5####_archives.tar.gz").suffixes
|
|
110
|
+
('.tar', '.gz')
|
|
111
|
+
|
|
112
|
+
>>> seq = LoosePathSequence("/path/to/images.exr.1-5####")
|
|
113
|
+
>>> seq[0]
|
|
114
|
+
PosixPath('/path/to/images.exr.0001')
|
|
115
|
+
>>> seq.suffixes
|
|
116
|
+
('.exr',)
|
|
117
|
+
|
|
118
|
+
>>> LoosePathSequence("/path/to/images.1001-1005<UDIM>.exr")[0]
|
|
119
|
+
PosixPath('/path/to/images.1001.exr')
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
Contributing
|
|
123
|
+
------------
|
|
124
|
+
|
|
125
|
+
Running the tests
|
|
126
|
+
~~~~~~~~~~~~~~~~~
|
|
127
|
+
|
|
128
|
+
Tests are executed through `tox <https://tox.readthedocs.io/en/latest/>`_.
|
|
129
|
+
|
|
130
|
+
.. code-block:: bash
|
|
131
|
+
|
|
132
|
+
tox
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
Code Style
|
|
136
|
+
~~~~~~~~~~
|
|
137
|
+
|
|
138
|
+
Code is formatted using `ruff <https://docs.astral.sh/ruff/formatter/>`_.
|
|
139
|
+
|
|
140
|
+
You can check your formatting using ruff's check mode:
|
|
141
|
+
|
|
142
|
+
.. code-block:: bash
|
|
143
|
+
|
|
144
|
+
tox -e format
|
|
145
|
+
|
|
146
|
+
You can also get ruff to format your changes for you:
|
|
147
|
+
|
|
148
|
+
.. code-block:: bash
|
|
149
|
+
|
|
150
|
+
tox -e autoformat
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
Release Notes
|
|
154
|
+
~~~~~~~~~~~~~
|
|
155
|
+
|
|
156
|
+
Release notes are managed through `towncrier <https://towncrier.readthedocs.io/en/stable/index.html>`_.
|
|
157
|
+
When making a pull request you will need to create a news fragment to document your change:
|
|
158
|
+
|
|
159
|
+
.. code-block:: bash
|
|
160
|
+
|
|
161
|
+
tox -e release_notes -- create --help
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
Versioning
|
|
165
|
+
----------
|
|
166
|
+
|
|
167
|
+
We use `SemVer <https://semver.org/>`_ for versioning.
|
|
168
|
+
For the versions available, see the `tags on this repository <https://github.com/AWhetter/pathseq/tags>`_.
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
License
|
|
172
|
+
-------
|
|
173
|
+
|
|
174
|
+
This project is licensed under the MIT License.
|
|
175
|
+
See the `LICENSE.rst <LICENSE.rst>`_ file for details.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.8.5,<1.0.0"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pathseq"
|
|
7
|
+
version = "1.0.0.post0"
|
|
8
|
+
description = "A pathlib-like library for working with file sequences."
|
|
9
|
+
readme = "README.rst"
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Environment :: Plugins",
|
|
13
|
+
"Framework :: Sphinx :: Extension",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Natural Language :: English",
|
|
17
|
+
"Programming Language :: Python",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
|
+
]
|
|
25
|
+
requires-python = ">=3.10"
|
|
26
|
+
dependencies = [
|
|
27
|
+
"lark",
|
|
28
|
+
"python-statemachine",
|
|
29
|
+
"typing_extensions",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[[project.authors]]
|
|
33
|
+
name = "Ashley Whetter"
|
|
34
|
+
email = "ashley@awhetter.co.uk"
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Source = "https://github.com/AWhetter/pathseq"
|
|
38
|
+
Documentation = "https://pathseq.readthedocs.org"
|
|
39
|
+
|
|
40
|
+
[dependency-groups]
|
|
41
|
+
test = [
|
|
42
|
+
"fileseq",
|
|
43
|
+
"hypothesis",
|
|
44
|
+
"pytest",
|
|
45
|
+
]
|
|
46
|
+
format = ["ruff"]
|
|
47
|
+
lint = ["ruff"]
|
|
48
|
+
typecheck = ["mypy"]
|
|
49
|
+
doc = [
|
|
50
|
+
"furo",
|
|
51
|
+
"sphinx",
|
|
52
|
+
"sphinx-design",
|
|
53
|
+
]
|
|
54
|
+
release_notes = ["towncrier"]
|
|
55
|
+
|
|
56
|
+
[[dependency-groups.dev]]
|
|
57
|
+
include-group = "test"
|
|
58
|
+
|
|
59
|
+
[[dependency-groups.dev]]
|
|
60
|
+
include-group = "format"
|
|
61
|
+
|
|
62
|
+
[[dependency-groups.dev]]
|
|
63
|
+
include-group = "lint"
|
|
64
|
+
|
|
65
|
+
[[dependency-groups.dev]]
|
|
66
|
+
include-group = "typecheck"
|
|
67
|
+
|
|
68
|
+
[[dependency-groups.dev]]
|
|
69
|
+
include-group = "doc"
|
|
70
|
+
|
|
71
|
+
[tool.uv.build-backend]
|
|
72
|
+
module-name = "pathseq"
|
|
73
|
+
|
|
74
|
+
[tool.mypy]
|
|
75
|
+
strict = true
|
|
76
|
+
|
|
77
|
+
[tool.towncrier]
|
|
78
|
+
directory = "doc/changes"
|
|
79
|
+
filename = "CHANGELOG.rst"
|
|
80
|
+
package = "pathseq"
|
|
81
|
+
title_format = "pathseq v{version} ({project_date})"
|
|
82
|
+
underlines = [
|
|
83
|
+
"-",
|
|
84
|
+
"^",
|
|
85
|
+
'"',
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
[tool.pytest.ini_options]
|
|
89
|
+
markers = ["todo"]
|
|
90
|
+
addopts = "--doctest-glob='[!0]*.rst' --doctest-modules --ignore=doc/source/conf.py"
|
|
91
|
+
doctest_optionflags = "ELLIPSIS"
|
|
92
|
+
testpaths = [
|
|
93
|
+
"tests",
|
|
94
|
+
"doc/source",
|
|
95
|
+
"src",
|
|
96
|
+
]
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.8.5,<1.0.0"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
1
5
|
[project]
|
|
2
6
|
name = "pathseq"
|
|
3
|
-
version = "0.
|
|
7
|
+
version = "1.0.0.post0"
|
|
4
8
|
description = "A pathlib-like library for working with file sequences."
|
|
5
9
|
authors = [
|
|
6
10
|
{name = "Ashley Whetter", email = "ashley@awhetter.co.uk"},
|
|
@@ -63,21 +67,18 @@ release_notes = [
|
|
|
63
67
|
"towncrier",
|
|
64
68
|
]
|
|
65
69
|
|
|
66
|
-
[build-system]
|
|
67
|
-
requires = ["uv_build>=0.8.5,<0.9.0"]
|
|
68
|
-
build-backend = "uv_build"
|
|
69
|
-
|
|
70
70
|
[tool.uv.build-backend]
|
|
71
71
|
module-name = "pathseq"
|
|
72
72
|
|
|
73
|
-
[tool.
|
|
74
|
-
|
|
73
|
+
[tool.mypy]
|
|
74
|
+
strict = true
|
|
75
75
|
|
|
76
76
|
[tool.towncrier]
|
|
77
77
|
directory = "doc/changes"
|
|
78
78
|
filename = "CHANGELOG.rst"
|
|
79
79
|
package = "pathseq"
|
|
80
80
|
title_format = "pathseq v{version} ({project_date})"
|
|
81
|
+
underlines = ["-", "^", "\""]
|
|
81
82
|
|
|
82
83
|
[tool.pytest.ini_options]
|
|
83
84
|
markers = [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""A pathlib-first library for working with file sequences."""
|
|
2
2
|
|
|
3
3
|
from ._ast import (
|
|
4
|
-
|
|
4
|
+
Formatter,
|
|
5
5
|
PaddedRange,
|
|
6
6
|
ParsedLooseSequence,
|
|
7
7
|
ParsedSequence,
|
|
@@ -11,27 +11,29 @@ from ._ast import (
|
|
|
11
11
|
RangesStartName,
|
|
12
12
|
)
|
|
13
13
|
from ._base import BasePathSequence, BasePurePathSequence, PathT_co, PurePathT_co
|
|
14
|
-
from ._error import
|
|
15
|
-
from ._file_num_seq import FileNumSequence
|
|
14
|
+
from ._error import NotASequenceError, ParseError
|
|
15
|
+
from ._file_num_seq import FileNumSequence, FileNumT
|
|
16
|
+
from ._from_disk import Completeness
|
|
16
17
|
from ._loose_path_sequence import LoosePathSequence
|
|
17
18
|
from ._loose_pure_path_sequence import LoosePurePathSequence
|
|
18
19
|
from ._path_sequence import PathSequence
|
|
19
20
|
from ._pure_path_sequence import PurePathSequence
|
|
20
21
|
|
|
21
|
-
__version__ = "0.
|
|
22
|
+
__version__ = "1.0.0.post0"
|
|
22
23
|
|
|
23
24
|
__all__ = (
|
|
24
25
|
"BasePathSequence",
|
|
25
26
|
"BasePurePathSequence",
|
|
27
|
+
"Completeness",
|
|
26
28
|
"FileNumSequence",
|
|
27
29
|
"FileNumT",
|
|
28
|
-
"
|
|
30
|
+
"Formatter",
|
|
29
31
|
"LoosePathSequence",
|
|
30
32
|
"LoosePurePathSequence",
|
|
31
33
|
"NotASequenceError",
|
|
32
34
|
"PaddedRange",
|
|
33
|
-
"ParsedLooseSequence",
|
|
34
35
|
"ParseError",
|
|
36
|
+
"ParsedLooseSequence",
|
|
35
37
|
"ParsedSequence",
|
|
36
38
|
"PathSequence",
|
|
37
39
|
"PathT_co",
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
from ._base import FileNum, FileNumT, PaddedRange, Ranges
|
|
2
1
|
from ._formatter import Formatter
|
|
3
2
|
from ._loose_type import (
|
|
4
3
|
ParsedLooseSequence,
|
|
5
|
-
RangesStartName,
|
|
6
|
-
RangesInName,
|
|
7
4
|
RangesEndName,
|
|
5
|
+
RangesInName,
|
|
6
|
+
RangesStartName,
|
|
8
7
|
)
|
|
8
|
+
from ._ranges import PaddedRange, Ranges
|
|
9
9
|
from ._type import ParsedSequence
|
|
10
|
+
from ._util import non_recursive_asdict
|
|
10
11
|
|
|
11
12
|
__all__ = [
|
|
12
|
-
"FileNum",
|
|
13
|
-
"FileNumT",
|
|
14
13
|
"Formatter",
|
|
15
14
|
"PaddedRange",
|
|
16
15
|
"ParsedLooseSequence",
|
|
17
16
|
"ParsedSequence",
|
|
18
17
|
"Ranges",
|
|
19
|
-
"RangesStartName",
|
|
20
|
-
"RangesInName",
|
|
21
18
|
"RangesEndName",
|
|
19
|
+
"RangesInName",
|
|
20
|
+
"RangesStartName",
|
|
21
|
+
"non_recursive_asdict",
|
|
22
22
|
]
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import typing
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
from decimal import Decimal
|
|
7
|
+
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
from ._loose_type import ParsedLooseSequence
|
|
10
|
+
from ._ranges import PaddedRange, Ranges
|
|
11
|
+
from ._type import ParsedSequence
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _splice_strings_onto_ranges(
|
|
15
|
+
strings: Iterable[str], inter_ranges: Iterable[str]
|
|
16
|
+
) -> str:
|
|
17
|
+
"""Format the given range strings and inter-range strings together.
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
TypeError: If the given number of strings does not match
|
|
21
|
+
the number of inter-range strings minus one.
|
|
22
|
+
"""
|
|
23
|
+
result = ""
|
|
24
|
+
|
|
25
|
+
strs = iter(strings)
|
|
26
|
+
for inter_range in inter_ranges:
|
|
27
|
+
try:
|
|
28
|
+
result += next(strs)
|
|
29
|
+
except StopIteration:
|
|
30
|
+
break
|
|
31
|
+
|
|
32
|
+
result += inter_range
|
|
33
|
+
else:
|
|
34
|
+
try:
|
|
35
|
+
result += next(strs)
|
|
36
|
+
except StopIteration:
|
|
37
|
+
pass
|
|
38
|
+
else:
|
|
39
|
+
try:
|
|
40
|
+
next(strs)
|
|
41
|
+
except StopIteration:
|
|
42
|
+
return result
|
|
43
|
+
|
|
44
|
+
raise TypeError(
|
|
45
|
+
"The number of inter-range strings given does not match"
|
|
46
|
+
" the number of range strings given minus one."
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Formatter:
|
|
51
|
+
"""A :class:`Formatter` formats a parsed sequence into a string.
|
|
52
|
+
|
|
53
|
+
The purpose of the class is primarily for converting from a
|
|
54
|
+
:ref:`simple <simple-format>` or :ref:`loose <loose-format>` sequence
|
|
55
|
+
into other sequence formats by overriding this class.
|
|
56
|
+
|
|
57
|
+
Example:
|
|
58
|
+
|
|
59
|
+
.. code-block:: pycon
|
|
60
|
+
|
|
61
|
+
>>> class HoudiniFormatter(pathseq.Formatter):
|
|
62
|
+
... def range(self, range_):
|
|
63
|
+
... return "$F"
|
|
64
|
+
...
|
|
65
|
+
>>> seq = pathseq.PathSequence("image.1-5#.exr")
|
|
66
|
+
>>> HoudiniFormatter().format(seq.parsed)
|
|
67
|
+
'image.$F.exr'
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self) -> None:
|
|
71
|
+
self.include_all_suffixes = False
|
|
72
|
+
"""Whether to include all suffixes in the formatted sequence.
|
|
73
|
+
|
|
74
|
+
This is set by :meth:`~.Formatter.format` to indicate whether the
|
|
75
|
+
suffixes have been cleanly separated from the stem or not.
|
|
76
|
+
If this is ``False``, then the ``stem`` and ``suffixes`` will overlap
|
|
77
|
+
and both include all but the last suffix.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def stem(self, stem: str) -> str:
|
|
81
|
+
return stem
|
|
82
|
+
|
|
83
|
+
def pre_range(self, pre_range: str) -> str:
|
|
84
|
+
return pre_range
|
|
85
|
+
|
|
86
|
+
def range(self, range_: PaddedRange[int] | PaddedRange[Decimal]) -> str:
|
|
87
|
+
return str(range_)
|
|
88
|
+
|
|
89
|
+
def inter_range(self, inter_range: str) -> str:
|
|
90
|
+
return inter_range
|
|
91
|
+
|
|
92
|
+
def ranges(self, ranges: Ranges) -> str:
|
|
93
|
+
return self.splice_inter_ranges(
|
|
94
|
+
(self.range(range_) for range_ in ranges.ranges),
|
|
95
|
+
(self.inter_range(inter_range) for inter_range in ranges.inter_ranges),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def post_range(self, post_range: str) -> str:
|
|
99
|
+
return post_range
|
|
100
|
+
|
|
101
|
+
def suffixes(self, suffixes: tuple[str, ...]) -> str:
|
|
102
|
+
return "".join(suffixes if self.include_all_suffixes else suffixes[-1:])
|
|
103
|
+
|
|
104
|
+
def format(self, seq: ParsedSequence | ParsedLooseSequence) -> str:
|
|
105
|
+
"""Format the given path sequence into a string.
|
|
106
|
+
|
|
107
|
+
The parsed sequence is traversed, and each node is formatted
|
|
108
|
+
according to the implementation of the associated method on this class.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
seq: The path sequence to format into a string.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
The formatter path sequence.
|
|
115
|
+
"""
|
|
116
|
+
from ._loose_type import RangesInName
|
|
117
|
+
from ._type import ParsedSequence
|
|
118
|
+
|
|
119
|
+
self.include_all_suffixes = bool(
|
|
120
|
+
isinstance(seq, ParsedSequence)
|
|
121
|
+
or (isinstance(seq, RangesInName) and seq.ranges.ranges)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return "".join(
|
|
125
|
+
getattr(self, field.name)(getattr(seq, field.name))
|
|
126
|
+
for field in dataclasses.fields(seq)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def splice_inter_ranges(
|
|
130
|
+
self, strings: Iterable[str], inter_ranges: Iterable[str]
|
|
131
|
+
) -> str:
|
|
132
|
+
"""Format the given range strings and inter-range strings together.
|
|
133
|
+
|
|
134
|
+
Raises:
|
|
135
|
+
TypeError: If the given number of strings does not match
|
|
136
|
+
the number of inter-range strings minus one.
|
|
137
|
+
"""
|
|
138
|
+
return _splice_strings_onto_ranges(strings, inter_ranges)
|