mtg-ssm 2.7.0__tar.gz → 2.7.1.dev5__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 (71) hide show
  1. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/.github/dependabot.yml +1 -1
  2. mtg_ssm-2.7.1.dev5/.github/workflows/integration.yml +48 -0
  3. mtg_ssm-2.7.1.dev5/.github/workflows/pre-commit.yml +23 -0
  4. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/.github/workflows/publish.yml +5 -13
  5. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/.github/workflows/run-tests.yml +15 -22
  6. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/.pre-commit-config.yaml +5 -5
  7. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/PKG-INFO +44 -37
  8. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/README.rst +37 -8
  9. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/TODO.md +0 -2
  10. mtg_ssm-2.7.1.dev5/mtg_ssm/_version.py +34 -0
  11. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/containers/bundles.py +20 -3
  12. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/containers/indexes.py +6 -7
  13. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/containers/legacy.py +3 -3
  14. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/scryfall/fetcher.py +4 -4
  15. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/scryfall/models.py +83 -90
  16. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/serialization/csv_serializer.py +4 -4
  17. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/serialization/interface.py +4 -4
  18. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/serialization/xlsx_serializer.py +28 -17
  19. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/ssm.py +1 -2
  20. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm.egg-info/PKG-INFO +44 -37
  21. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm.egg-info/SOURCES.txt +2 -0
  22. mtg_ssm-2.7.1.dev5/mtg_ssm.egg-info/requires.txt +9 -0
  23. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/pyproject.toml +10 -14
  24. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/containers/test_bundles.py +82 -1
  25. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/data/bulk_data.json +2 -2
  26. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/data/cards.json +2304 -996
  27. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/data/sets.json +171 -43
  28. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/data/sets1.json +94 -21
  29. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/data/sets2.json +95 -40
  30. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/gen_testdata.py +9 -2
  31. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/scryfall/test_fetcher.py +6 -4
  32. mtg_ssm-2.7.1.dev5/tests/scryfall/test_models.py +26 -0
  33. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/serialization/__snapshots__/test_xlsx_serializer.ambr +211 -211
  34. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/serialization/test_csv_serializer.py +2 -6
  35. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/serialization/test_xlsx_serializer.py +2 -3
  36. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/test_ssm.py +44 -106
  37. mtg_ssm-2.7.1.dev5/uv.lock +1308 -0
  38. mtg_ssm-2.7.0/.github/workflows/integration.yml +0 -50
  39. mtg_ssm-2.7.0/.github/workflows/pre-commit.yml +0 -26
  40. mtg_ssm-2.7.0/mtg_ssm/_version.py +0 -21
  41. mtg_ssm-2.7.0/mtg_ssm.egg-info/requires.txt +0 -32
  42. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/.editorconfig +0 -0
  43. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/.gitignore +0 -0
  44. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/LICENSE.txt +0 -0
  45. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/__init__.py +0 -0
  46. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/containers/__init__.py +0 -0
  47. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/containers/collection.py +0 -0
  48. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/containers/counts.py +0 -0
  49. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/mtg/__init__.py +0 -0
  50. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/mtg/util.py +0 -0
  51. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/py.typed +0 -0
  52. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/scryfall/__init__.py +0 -0
  53. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm/serialization/__init__.py +0 -0
  54. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm.egg-info/dependency_links.txt +0 -0
  55. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm.egg-info/entry_points.txt +0 -0
  56. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/mtg_ssm.egg-info/top_level.txt +0 -0
  57. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/setup.cfg +0 -0
  58. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/__init__.py +0 -0
  59. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/conftest.py +0 -0
  60. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/containers/__init__.py +0 -0
  61. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/containers/test_counts.py +0 -0
  62. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/containers/test_indexes.py +0 -0
  63. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/containers/test_legacy.py +0 -0
  64. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/data/LICENSE.txt +0 -0
  65. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/data/migrations.json +0 -0
  66. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/mtg/__init__.py +0 -0
  67. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/mtg/test_util.py +0 -0
  68. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/scryfall/__init__.py +0 -0
  69. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/serialization/__init__.py +0 -0
  70. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/serialization/__snapshots__/test_csv_serializer.ambr +0 -0
  71. {mtg_ssm-2.7.0 → mtg_ssm-2.7.1.dev5}/tests/serialization/test_interface.py +0 -0
@@ -5,7 +5,7 @@ updates:
5
5
  schedule:
6
6
  interval: weekly
7
7
 
8
- - package-ecosystem: pip
8
+ - package-ecosystem: uv
9
9
  directory: /
10
10
  schedule:
11
11
  interval: daily
@@ -0,0 +1,48 @@
1
+ name: integration
2
+ concurrency: integration
3
+
4
+ on:
5
+ push:
6
+ branches:
7
+ # Run on every push to trunk
8
+ - trunk
9
+ schedule:
10
+ # Run every Saturday at 4:05 AM UTC
11
+ - cron: "5 4 * * SAT"
12
+
13
+ jobs:
14
+ end-to-end:
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ matrix:
18
+ include:
19
+ - name: base
20
+ sync-args: ""
21
+ - name: lxml
22
+ sync-args: "--extra lxml"
23
+ name: End-to-end tests (${{ matrix.name }})
24
+ steps:
25
+ - uses: actions/checkout@v6
26
+
27
+ - uses: astral-sh/setup-uv@v7
28
+ with:
29
+ enable-cache: true
30
+ python-version: "3.14"
31
+
32
+ - name: Sync runtime environment
33
+ run: uv sync --frozen --no-dev ${{ matrix.sync-args }}
34
+
35
+ - name: Create csv
36
+ run: uv run --frozen --no-dev mtg-ssm create collection1.csv
37
+
38
+ - name: Update csv
39
+ run: uv run --frozen --no-dev mtg-ssm update collection1.csv
40
+
41
+ - name: Create xlsx
42
+ run: uv run --frozen --no-dev mtg-ssm create collection2.xlsx
43
+
44
+ - name: Update xlsx
45
+ run: uv run --frozen --no-dev mtg-ssm update collection2.xlsx
46
+
47
+ - name: Merge csv and xlsx
48
+ run: uv run --frozen --no-dev mtg-ssm merge collection3.xlsx collection1.csv collection2.xlsx
@@ -0,0 +1,23 @@
1
+ name: lint
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - trunk
8
+
9
+ jobs:
10
+ pre-commit:
11
+ runs-on: ubuntu-latest
12
+ name: Run all pre-commit checks
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+
16
+ - uses: astral-sh/setup-uv@v7
17
+ with:
18
+ enable-cache: true
19
+ python-version: "3.14"
20
+
21
+ - run: uv sync --frozen
22
+
23
+ - run: uv run --frozen pre-commit run --all-files
@@ -16,26 +16,18 @@ jobs:
16
16
  id-token: write
17
17
 
18
18
  steps:
19
- - uses: actions/checkout@v4
19
+ - uses: actions/checkout@v6
20
20
  with:
21
21
  # setuptools_scm needs the git history
22
22
  fetch-depth: 0
23
23
 
24
- - uses: actions/setup-python@v5
24
+ - uses: astral-sh/setup-uv@v7
25
25
  with:
26
- python-version: "3.9"
27
-
28
- - name: Install pypa/build
29
- run: >-
30
- pip install --user
31
- build
26
+ enable-cache: true
27
+ python-version: "3.14"
32
28
 
33
29
  - name: Build a binary wheel and a source tarball
34
- run: >-
35
- python -m build
36
- --sdist
37
- --wheel
38
- --outdir dist/
30
+ run: uv build --sdist --wheel --out-dir dist
39
31
 
40
32
  - name: Publish distribution 📦 to Test PyPI
41
33
  uses: pypa/gh-action-pypi-publish@release/v1.12
@@ -11,11 +11,11 @@ jobs:
11
11
  strategy:
12
12
  matrix:
13
13
  python-version:
14
- - "3.9"
15
14
  - "3.10"
16
15
  - "3.11"
17
16
  - "3.12"
18
17
  - "3.13"
18
+ - "3.14"
19
19
  os:
20
20
  - ubuntu-latest
21
21
  - macos-latest
@@ -23,52 +23,46 @@ jobs:
23
23
  runs-on: ${{ matrix.os }}
24
24
  name: Can run on ${{ matrix.os }} - Python ${{ matrix.python-version }}
25
25
  steps:
26
- - uses: actions/checkout@v4
26
+ - uses: actions/checkout@v6
27
27
 
28
- - uses: actions/setup-python@v5
28
+ - uses: astral-sh/setup-uv@v7
29
29
  with:
30
+ enable-cache: true
30
31
  python-version: "${{ matrix.python-version }}"
31
- cache: pip
32
- cache-dependency-path: pyproject.toml
33
32
 
34
- - name: Install project
35
- run: |
36
- pip install -U pip setuptools
37
- pip install -e .
33
+ - name: Sync runtime environment
34
+ run: uv sync --frozen --no-dev
38
35
 
39
36
  - name: Run mtg-ssm
40
- run: mtg-ssm --help
37
+ run: uv run --frozen --no-dev mtg-ssm --help
41
38
 
42
39
  pytest:
43
40
  runs-on: ubuntu-latest
44
41
  strategy:
45
42
  matrix:
46
43
  python-version:
47
- - "3.9"
48
44
  - "3.10"
49
45
  - "3.11"
50
46
  - "3.12"
51
47
  - "3.13"
48
+ - "3.14"
52
49
  name: Python ${{ matrix.python-version }} pytest
53
50
  steps:
54
- - uses: actions/checkout@v4
51
+ - uses: actions/checkout@v6
55
52
 
56
- - uses: actions/setup-python@v5
53
+ - uses: astral-sh/setup-uv@v7
57
54
  with:
55
+ enable-cache: true
58
56
  python-version: "${{ matrix.python-version }}"
59
- cache: pip
60
- cache-dependency-path: pyproject.toml
61
57
 
62
- - name: Install project
63
- run: |
64
- pip install -U pip setuptools wheel
65
- pip install -e .[lxml,dev]
58
+ - name: Sync test environment
59
+ run: uv sync --frozen --extra lxml
66
60
 
67
61
  - name: Execute pytest
68
- run: pytest tests/
62
+ run: uv run --frozen pytest tests/
69
63
 
70
64
  - name: Convert coverage
71
- run: coverage lcov
65
+ run: uv run --frozen coverage lcov
72
66
 
73
67
  - name: Coveralls Parallel
74
68
  uses: coverallsapp/github-action@v2.3.6
@@ -87,5 +81,4 @@ jobs:
87
81
  uses: coverallsapp/github-action@v2.3.6
88
82
  with:
89
83
  github-token: ${{ secrets.github_token }}
90
- path-to-lcov: coverage.lcov
91
84
  parallel-finished: true
@@ -4,19 +4,19 @@ repos:
4
4
  - id: black
5
5
  name: black
6
6
  description: "Black: The uncompromising Python code formatter"
7
- entry: black
7
+ entry: uv run --frozen black
8
8
  language: system
9
9
  require_serial: true
10
10
  types: [python]
11
11
  - id: mypy
12
12
  name: Type checking from mypy
13
13
  language: system
14
- entry: mypy
14
+ entry: uv run --frozen mypy
15
15
  types: [python]
16
16
  - id: ruff
17
17
  name: ruff
18
18
  description: "Run ruff on files"
19
- entry: ruff check --force-exclude
19
+ entry: uv run --frozen ruff check --force-exclude
20
20
  language: system
21
21
  types_or: [python, pyi]
22
22
  args: [--fix, --exit-non-zero-on-fix]
@@ -24,12 +24,12 @@ repos:
24
24
  - id: doc8-python
25
25
  name: Documentation checking from doc8 (Python)
26
26
  language: system
27
- entry: doc8
27
+ entry: uv run --frozen doc8
28
28
  types: [python]
29
29
  - id: doc8-rst
30
30
  name: Documentation checking from doc8 (reStructuredText)
31
31
  language: system
32
- entry: doc8
32
+ entry: uv run --frozen doc8
33
33
  types: [rst]
34
34
  - id: eclint
35
35
  name: EditorConfig file validation
@@ -1,16 +1,15 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: mtg_ssm
3
- Version: 2.7.0
3
+ Version: 2.7.1.dev5
4
4
  Summary: A tool to manage Magic: the Gathering collection spreadsheets
5
5
  Author-email: George Leslie-Waksman <waksman@gmail.com>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/gwax/mtg_ssm
8
8
  Project-URL: Bug Tracker, https://github.com/gwax/mtg_ssm/issues
9
9
  Keywords: mtg,magic,collection,tracking,spreadsheet
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Environment :: Console
12
12
  Classifier: Intended Audience :: End Users/Desktop
13
- Classifier: License :: OSI Approved :: MIT License
14
13
  Classifier: Operating System :: MacOS
15
14
  Classifier: Operating System :: Microsoft :: Windows
16
15
  Classifier: Operating System :: OS Independent
@@ -19,45 +18,24 @@ Classifier: Operating System :: Unix
19
18
  Classifier: Programming Language :: Python
20
19
  Classifier: Programming Language :: Python :: 3
21
20
  Classifier: Programming Language :: Python :: 3 :: Only
22
- Classifier: Programming Language :: Python :: 3.9
23
21
  Classifier: Programming Language :: Python :: 3.10
24
22
  Classifier: Programming Language :: Python :: 3.11
25
23
  Classifier: Programming Language :: Python :: 3.12
26
24
  Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
27
26
  Classifier: Topic :: Games/Entertainment
28
- Requires-Python: >=3.9
27
+ Requires-Python: >=3.10
29
28
  Description-Content-Type: text/x-rst
30
29
  License-File: LICENSE.txt
31
- Requires-Dist: appdirs~=1.4
32
30
  Requires-Dist: msgspec~=0.15
33
31
  Requires-Dist: openpyxl~=3.1
32
+ Requires-Dist: platformdirs~=4.0
34
33
  Requires-Dist: requests~=2.27
35
34
  Requires-Dist: requests-cache~=0.9.8
36
35
  Requires-Dist: typing_extensions~=4.8.0
37
36
  Provides-Extra: lxml
38
37
  Requires-Dist: lxml>=3.7.2; extra == "lxml"
39
- Provides-Extra: dev
40
- Requires-Dist: black; extra == "dev"
41
- Requires-Dist: coverage[toml]; extra == "dev"
42
- Requires-Dist: doc8; extra == "dev"
43
- Requires-Dist: exceptiongroup; extra == "dev"
44
- Requires-Dist: freezegun; extra == "dev"
45
- Requires-Dist: lxml; extra == "dev"
46
- Requires-Dist: mypy; extra == "dev"
47
- Requires-Dist: openpyxl-stubs; extra == "dev"
48
- Requires-Dist: pip; extra == "dev"
49
- Requires-Dist: Pygments; extra == "dev"
50
- Requires-Dist: pytest>=6.0; extra == "dev"
51
- Requires-Dist: pytest-cov; extra == "dev"
52
- Requires-Dist: responses; extra == "dev"
53
- Requires-Dist: ruff; extra == "dev"
54
- Requires-Dist: setuptools>=45; extra == "dev"
55
- Requires-Dist: setuptools-scm[toml]>=6.2; extra == "dev"
56
- Requires-Dist: syrupy>=4.0; extra == "dev"
57
- Requires-Dist: types-appdirs; extra == "dev"
58
- Requires-Dist: types-freezegun; extra == "dev"
59
- Requires-Dist: types-requests; extra == "dev"
60
- Requires-Dist: wheel; extra == "dev"
38
+ Dynamic: license-file
61
39
 
62
40
  mtg-ssm - Magic: the Gathering Spreadsheet Manager
63
41
  ===================================================
@@ -102,19 +80,28 @@ Please also check out my other major Magic: the Gathering project,
102
80
  Installation
103
81
  ============
104
82
 
105
- mtg-ssm is available on PyPI so, if you have python (>=3.6) and pip
106
- installed on your system, you should be able to get mtg-ssm by entering
107
- the following into a terminal:
83
+ mtg-ssm is available on PyPI so, if you have Python (>=3.10) and a
84
+ package installer such as uv or pip installed on your system, you
85
+ should be able to get mtg-ssm by entering one of the following into a
86
+ terminal:
108
87
 
109
88
  .. code:: bash
110
89
 
111
- pip3 install mtg_ssm
90
+ uv tool install mtg_ssm
91
+
92
+ .. code:: bash
93
+
94
+ pip install mtg_ssm
112
95
 
113
96
  Updates can be performed by entering:
114
97
 
115
98
  .. code:: bash
116
99
 
117
- pip3 install -U mtg_ssm
100
+ uv tool upgrade mtg-ssm
101
+
102
+ .. code:: bash
103
+
104
+ pip install -U mtg_ssm
118
105
 
119
106
  You can verify installation from the terminal by running:
120
107
 
@@ -210,8 +197,26 @@ Contributions
210
197
 
211
198
  Pull requests are welcome and contributions are greatly appreciated. If you
212
199
  would like to contribute, please be sure that all tests and lint checks
213
- pass. Also consider running `python -m tests.gen_testdata` to ensure that
214
- everything is up to date and works.
200
+ pass.
201
+
202
+ Development uses `uv`. A typical local setup is:
203
+
204
+ .. code:: bash
205
+
206
+ uv python install 3.14
207
+ uv sync --python 3.14 --extra lxml
208
+
209
+ Common development commands:
210
+
211
+ .. code:: bash
212
+
213
+ uv run --python 3.14 pre-commit run --all-files
214
+ uv run --python 3.14 pytest
215
+ uv run --python 3.14 python -m tests.gen_testdata
216
+
217
+ The package metadata and lint/type-check targets stay on Python 3.10, so
218
+ changes should remain compatible with that floor even when developed from
219
+ a Python 3.14 environment.
215
220
 
216
221
  Issues can be reported via GitHub.
217
222
 
@@ -238,7 +243,9 @@ Changelog
238
243
  Development
239
244
  -----------
240
245
 
241
- - ...
246
+ - Switch project development and CI workflows to uv.
247
+ - Set Python 3.10 as the supported package floor.
248
+ - Use Python 3.14 for development workflows.
242
249
 
243
250
  2.7.0
244
251
  -----
@@ -41,19 +41,28 @@ Please also check out my other major Magic: the Gathering project,
41
41
  Installation
42
42
  ============
43
43
 
44
- mtg-ssm is available on PyPI so, if you have python (>=3.6) and pip
45
- installed on your system, you should be able to get mtg-ssm by entering
46
- the following into a terminal:
44
+ mtg-ssm is available on PyPI so, if you have Python (>=3.10) and a
45
+ package installer such as uv or pip installed on your system, you
46
+ should be able to get mtg-ssm by entering one of the following into a
47
+ terminal:
47
48
 
48
49
  .. code:: bash
49
50
 
50
- pip3 install mtg_ssm
51
+ uv tool install mtg_ssm
52
+
53
+ .. code:: bash
54
+
55
+ pip install mtg_ssm
51
56
 
52
57
  Updates can be performed by entering:
53
58
 
54
59
  .. code:: bash
55
60
 
56
- pip3 install -U mtg_ssm
61
+ uv tool upgrade mtg-ssm
62
+
63
+ .. code:: bash
64
+
65
+ pip install -U mtg_ssm
57
66
 
58
67
  You can verify installation from the terminal by running:
59
68
 
@@ -149,8 +158,26 @@ Contributions
149
158
 
150
159
  Pull requests are welcome and contributions are greatly appreciated. If you
151
160
  would like to contribute, please be sure that all tests and lint checks
152
- pass. Also consider running `python -m tests.gen_testdata` to ensure that
153
- everything is up to date and works.
161
+ pass.
162
+
163
+ Development uses `uv`. A typical local setup is:
164
+
165
+ .. code:: bash
166
+
167
+ uv python install 3.14
168
+ uv sync --python 3.14 --extra lxml
169
+
170
+ Common development commands:
171
+
172
+ .. code:: bash
173
+
174
+ uv run --python 3.14 pre-commit run --all-files
175
+ uv run --python 3.14 pytest
176
+ uv run --python 3.14 python -m tests.gen_testdata
177
+
178
+ The package metadata and lint/type-check targets stay on Python 3.10, so
179
+ changes should remain compatible with that floor even when developed from
180
+ a Python 3.14 environment.
154
181
 
155
182
  Issues can be reported via GitHub.
156
183
 
@@ -177,7 +204,9 @@ Changelog
177
204
  Development
178
205
  -----------
179
206
 
180
- - ...
207
+ - Switch project development and CI workflows to uv.
208
+ - Set Python 3.10 as the supported package floor.
209
+ - Use Python 3.14 for development workflows.
181
210
 
182
211
  2.7.0
183
212
  -----
@@ -4,5 +4,3 @@
4
4
  - Group sets by parent set is xlsx output?
5
5
  - Block? probably not
6
6
  - diffs to stdout
7
- - Update card_count for sets when processing bundle filters
8
- - Switch to hatchling as our build system?
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '2.7.1.dev5'
32
+ __version_tuple__ = version_tuple = (2, 7, 1, 'dev5')
33
+
34
+ __commit_id__ = commit_id = 'g6ae45693f'
@@ -1,7 +1,8 @@
1
1
  """Data bundle definitions."""
2
2
 
3
3
  import copy
4
- from typing import NamedTuple, Optional
4
+ from collections import Counter
5
+ from typing import NamedTuple
5
6
 
6
7
  from mtg_ssm.scryfall.models import ScryCard, ScryCardLayout, ScryMigration, ScrySet, ScrySetType
7
8
 
@@ -14,11 +15,26 @@ class ScryfallDataSet(NamedTuple):
14
15
  migrations: list[ScryMigration]
15
16
 
16
17
 
18
+ def recount_sets(sets: list[ScrySet], cards: list[ScryCard]) -> list[ScrySet]:
19
+ """Return sets with card counts updated to match the provided card list."""
20
+ set_code_to_count = Counter(card.set for card in cards)
21
+ recounted_sets = []
22
+ for set_ in sets:
23
+ card_count = set_code_to_count[set_.code]
24
+ if set_.card_count == card_count:
25
+ recounted_sets.append(set_)
26
+ continue
27
+ recounted_set = copy.copy(set_)
28
+ recounted_set.card_count = card_count
29
+ recounted_sets.append(recounted_set)
30
+ return recounted_sets
31
+
32
+
17
33
  def filter_cards_and_sets( # noqa: C901
18
34
  scryfall_data: ScryfallDataSet,
19
35
  *,
20
- exclude_set_types: Optional[set[ScrySetType]] = None,
21
- exclude_card_layouts: Optional[set[ScryCardLayout]] = None,
36
+ exclude_set_types: set[ScrySetType] | None = None,
37
+ exclude_card_layouts: set[ScryCardLayout] | None = None,
22
38
  exclude_digital: bool = False,
23
39
  exclude_foreing_only: bool = False,
24
40
  merge_promos: bool = False,
@@ -68,6 +84,7 @@ def filter_cards_and_sets( # noqa: C901
68
84
  for s in scryfall_data.sets
69
85
  if s.code in accepted_setcodes and s.code in nonempty_setcodes
70
86
  ]
87
+ accepted_sets = recount_sets(accepted_sets, accepted_cards)
71
88
 
72
89
  return ScryfallDataSet(
73
90
  sets=accepted_sets,
@@ -3,7 +3,6 @@
3
3
  import collections
4
4
  import string
5
5
  from collections.abc import Iterable
6
- from typing import Optional
7
6
  from uuid import UUID
8
7
 
9
8
  from mtg_ssm.containers.bundles import ScryfallDataSet
@@ -23,9 +22,9 @@ def set_card_sort_key(card: ScryCard) -> tuple[str, int, str]:
23
22
 
24
23
  def build_snnmas(
25
24
  card: ScryCard,
26
- ) -> Iterable[tuple[Optional[str], str, Optional[str], Optional[int], Optional[str]]]:
25
+ ) -> Iterable[tuple[str | None, str, str | None, int | None, str | None]]:
27
26
  """Build set, name, number, multiverse id tuple keys."""
28
- names_cnums: set[tuple[str, Optional[str]]] = {(card.name, card.collector_number)}
27
+ names_cnums: set[tuple[str, str | None]] = {(card.name, card.collector_number)}
29
28
  for i, card_face in enumerate(card.card_faces or ()):
30
29
  names_cnums |= {
31
30
  (card_face.name, card.collector_number),
@@ -33,11 +32,11 @@ def build_snnmas(
33
32
  }
34
33
  names_cnums |= {(n, None) for n, _ in names_cnums}
35
34
 
36
- sets: set[Optional[str]] = {card.set, None}
35
+ sets: set[str | None] = {card.set, None}
37
36
 
38
- mvids: set[Optional[int]] = {None} | set(card.multiverse_ids or ())
37
+ mvids: set[int | None] = {None} | set(card.multiverse_ids or ())
39
38
 
40
- artists: set[Optional[str]] = {card.artist, None}
39
+ artists: set[str | None] = {card.artist, None}
41
40
 
42
41
  for name, number in names_cnums:
43
42
  for set_ in sets:
@@ -60,7 +59,7 @@ class ScryfallDataIndex:
60
59
  # TODO: convert to intersecting bitmap indexes
61
60
  # TODO: do we really need artist?
62
61
  self.snnma_to_id: dict[
63
- tuple[Optional[str], str, Optional[str], Optional[int], Optional[str]],
62
+ tuple[str | None, str, str | None, int | None, str | None],
64
63
  set[UUID],
65
64
  ] = {}
66
65
 
@@ -1,6 +1,6 @@
1
1
  """Legacy record lookup capabilities for older file versions."""
2
2
 
3
- from typing import Any, Optional
3
+ from typing import Any
4
4
  from uuid import UUID
5
5
 
6
6
  from mtg_ssm.containers.indexes import Oracle
@@ -93,7 +93,7 @@ OTHER_SET_CODE_TO_SET_CODE = {
93
93
  "PCA": ["opca"],
94
94
  }
95
95
 
96
- PSUDONYM_TO_ARTIST: dict[Optional[str], str] = {
96
+ PSUDONYM_TO_ARTIST: dict[str | None, str] = {
97
97
  "William Murai": "Willian Murai",
98
98
  "Dave Seeley": "David Seeley",
99
99
  }
@@ -113,7 +113,7 @@ def find_scryfall_id(card_row: dict[str, str], oracle: Oracle) -> UUID:
113
113
  artist = card_row.get("artist") or None
114
114
  artist = PSUDONYM_TO_ARTIST.get(artist, artist)
115
115
  print(f"Searching => Set: {set_code}; Name: {name}; Number: {collector_number}; MVID: {mvid}")
116
- snnma_keys: list[tuple[Optional[str], str, Optional[str], Optional[int], Optional[str]]] = []
116
+ snnma_keys: list[tuple[str | None, str, str | None, int | None, str | None]] = []
117
117
  for set_ in set_codes:
118
118
  snnma_keys += [
119
119
  (set_, name, collector_number, None, None),
@@ -1,10 +1,10 @@
1
1
  """Scryfall data fetcher."""
2
2
 
3
3
  import gzip
4
- import os
4
+ from pathlib import Path
5
5
 
6
- import appdirs
7
6
  import msgspec
7
+ from platformdirs import user_cache_dir
8
8
  from requests_cache import CachedSession, SerializerPipeline, Stage, pickle_serializer
9
9
 
10
10
  from mtg_ssm.containers.bundles import ScryfallDataSet
@@ -12,7 +12,7 @@ from mtg_ssm.scryfall.models import ScryBulkData, ScryCard, ScryList, ScryMigrat
12
12
 
13
13
  APP_AUTHOR = "gwax"
14
14
  APP_NAME = "mtg_ssm"
15
- CACHE_DIR = appdirs.user_cache_dir(APP_NAME, APP_AUTHOR)
15
+ CACHE_DIR = Path(user_cache_dir(APP_NAME, APP_AUTHOR))
16
16
  CACHE_SERIALIZER = SerializerPipeline(
17
17
  [
18
18
  pickle_serializer,
@@ -21,7 +21,7 @@ CACHE_SERIALIZER = SerializerPipeline(
21
21
  is_binary=True,
22
22
  )
23
23
  SESSION = CachedSession(
24
- os.path.join(CACHE_DIR, "requests_cache.sqlite"), # noqa: PTH118
24
+ str(CACHE_DIR / "requests_cache.sqlite"),
25
25
  backend="sqlite",
26
26
  serializer=CACHE_SERIALIZER,
27
27
  cache_control=True,