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.
Files changed (31) hide show
  1. {pathseq-0.1.0 → pathseq-1.0.0.post0}/PKG-INFO +82 -14
  2. pathseq-1.0.0.post0/README.rst +175 -0
  3. pathseq-1.0.0.post0/pyproject.toml +96 -0
  4. pathseq-0.1.0/pyproject.toml → pathseq-1.0.0.post0/pyproject.toml.orig +8 -7
  5. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/__init__.py +8 -6
  6. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_ast/__init__.py +7 -7
  7. pathseq-1.0.0.post0/src/pathseq/_ast/_formatter.py +138 -0
  8. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_ast/_loose_type.py +24 -26
  9. pathseq-1.0.0.post0/src/pathseq/_ast/_ranges.py +72 -0
  10. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_ast/_type.py +9 -10
  11. pathseq-1.0.0.post0/src/pathseq/_ast/_util.py +98 -0
  12. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_base.py +61 -26
  13. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_error.py +0 -24
  14. pathseq-1.0.0.post0/src/pathseq/_file_num_seq/__init__.py +7 -0
  15. {pathseq-0.1.0/src/pathseq → pathseq-1.0.0.post0/src/pathseq/_file_num_seq}/_arithmetic_sequence.py +8 -9
  16. {pathseq-0.1.0/src/pathseq → pathseq-1.0.0.post0/src/pathseq/_file_num_seq}/_decimal_range.py +14 -12
  17. {pathseq-0.1.0/src/pathseq → pathseq-1.0.0.post0/src/pathseq/_file_num_seq}/_file_num_seq.py +8 -7
  18. {pathseq-0.1.0/src/pathseq → pathseq-1.0.0.post0/src/pathseq/_file_num_seq}/_parse_file_num_seq.py +6 -6
  19. pathseq-0.1.0/src/pathseq/_formatter.py → pathseq-1.0.0.post0/src/pathseq/_formatters.py +3 -3
  20. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_from_disk.py +62 -25
  21. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_loose_pure_path_sequence.py +10 -35
  22. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_parse_loose_path_sequence.py +124 -77
  23. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_parse_path_sequence.py +25 -22
  24. pathseq-1.0.0.post0/src/pathseq/_pure_path_sequence.py +25 -0
  25. pathseq-0.1.0/README.rst +0 -107
  26. pathseq-0.1.0/src/pathseq/_ast/_base.py +0 -236
  27. pathseq-0.1.0/src/pathseq/_ast/_formatter.py +0 -38
  28. pathseq-0.1.0/src/pathseq/_pure_path_sequence.py +0 -58
  29. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_loose_path_sequence.py +0 -0
  30. {pathseq-0.1.0 → pathseq-1.0.0.post0}/src/pathseq/_path_sequence.py +0 -0
  31. {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.1.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 file sequences.
47
+ A pathlib-first library for working with path sequences.
48
48
 
49
- TODO: Note special features
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
- * Sequence compression?
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 through pip:
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 `black <https://github.com/python/black>`_.
165
+ Code is formatted using `ruff <https://docs.astral.sh/ruff/formatter/>`_.
98
166
 
99
- You can check your formatting using black's check mode:
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 black to format your changes for you:
173
+ You can also get ruff to format your changes for you:
106
174
 
107
175
  .. code-block:: bash
108
176
 
109
- tox -e format -- src/ tests/
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.1.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.pylint]
74
- disable = "R,unused-argument"
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
- FileNumT,
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 IncompleteDimensionError, NotASequenceError, ParseError
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.1.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
- "IncompleteDimensionError",
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)