├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── dependabot.yml
└── workflows
│ ├── testing.yml
│ └── validation.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── drafts
└── v1.1
│ ├── MetronInfo.xsd
│ └── Sample.xml
├── poetry.lock
├── pyproject.toml
├── schema
└── v1.0
│ ├── MetronInfo.xsd
│ └── Sample.xml
└── tests
├── test.py
└── test_files
└── v1.0
├── dup_primary_attr.xml
└── valid.xml
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ""
5 | labels: ""
6 | assignees: ""
7 | ---
8 |
9 | **Describe the bug** A clear and concise description of what the bug is.
10 |
11 | **To Reproduce** Steps to reproduce the behavior:
12 |
13 | 1. Go to '...'
14 | 2. Click on '....'
15 | 3. Scroll down to '....'
16 | 4. See error
17 |
18 | **Expected behavior** A clear and concise description of what you expected to
19 | happen.
20 |
21 | **Screenshots** If applicable, add screenshots to help explain your problem.
22 |
23 | **Additional context** Add any other context about the problem here.
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ""
5 | labels: ""
6 | assignees: ""
7 | ---
8 |
9 | **Is your feature request related to a problem? Please describe.** A clear and
10 | concise description of what the problem is. Ex. I'm always frustrated when [...]
11 |
12 | **Describe the solution you'd like** A clear and concise description of what you
13 | want to happen.
14 |
15 | **Describe alternatives you've considered** A clear and concise description of
16 | any alternative solutions or features you've considered.
17 |
18 | **Additional context** Add any other context or screenshots about the feature
19 | request here.
20 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 |
4 | # Maintain dependencies for GitHub Actions
5 | - package-ecosystem: "github-actions"
6 | directory: "/"
7 | schedule:
8 | interval: "weekly"
--------------------------------------------------------------------------------
/.github/workflows/testing.yml:
--------------------------------------------------------------------------------
1 | name: "Testing"
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | tox:
13 | name: "Tests"
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | python-version: ["3.13"]
18 | os:
19 | - ubuntu-latest
20 | runs-on: ${{ matrix.os }}
21 | steps:
22 | #----------------------------------------------
23 | # check-out repo and set-up python
24 | #----------------------------------------------
25 | - name: Checkout
26 | uses: actions/checkout@v4
27 | - name: Setup python
28 | uses: actions/setup-python@v5
29 | with:
30 | python-version: ${{ matrix.python-version }}
31 | #----------------------------------------------
32 | # ----- install & configure poetry -----
33 | #----------------------------------------------
34 | - name: Install Poetry
35 | uses: snok/install-poetry@v1
36 | with:
37 | version: 1.8.4
38 | virtualenvs-create: true
39 | virtualenvs-in-project: true
40 | #----------------------------------------------
41 | # load cached venv if cache exists
42 | #----------------------------------------------
43 | - name: Load cached venv
44 | id: cached-poetry-dependencies
45 | uses: actions/cache@v4
46 | with:
47 | path: .venv
48 | key: venv-${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
49 | #----------------------------------------------
50 | # install dependencies if cache does not exist
51 | #----------------------------------------------
52 | - name: Install dependencies
53 | if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
54 | run: poetry install --no-interaction --no-root
55 | #----------------------------------------------
56 | # install your root project, if required
57 | #----------------------------------------------
58 | - name: Install library
59 | run: poetry install --no-interaction
60 | #----------------------------------------------
61 | # Run pytest
62 | #----------------------------------------------
63 | - name: Test with pytest
64 | run: poetry run pytest
65 |
--------------------------------------------------------------------------------
/.github/workflows/validation.yml:
--------------------------------------------------------------------------------
1 | name: XSD Validation
2 |
3 | on:
4 | push:
5 | paths:
6 | - '**.xsd'
7 | pull_request:
8 | paths:
9 | - '**.xsd'
10 |
11 | jobs:
12 | validate:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | - name: apt-get update
17 | run: sudo apt-get update -y
18 | - name: Install xmllint
19 | run: sudo apt-get install -y libxml2-utils
20 | - name: Validate XSD Schemas
21 | run: find . -type f -name "*.xsd" -exec xmllint -noout -schema http://www.w3.org/2009/XMLSchema/XMLSchema.xsd {} +
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | bpepple@metron.cloud.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Brian Pepple
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MetronInfo.xml
2 |
3 | ## What is it?
4 |
5 | `MetronInfo.xml` is an attempt to create a new schema for digital comic books that fixes some of the deficiencies that
6 | exist with the `ComicInfo.xml` schema.
7 |
8 | ## Rationale
9 |
10 | The `ComicInfo.xml` schema was designed for the needs of the ComicRack Application, and supports a fairly limited amount
11 | of data. Some benefits of a new schema would include:
12 |
13 | - Additional `Elements` for information. (e.g. Price, Global Trade Item Numbers, Series Type, etc.)
14 | - Better handling of data types. Instead of using delimited strings for list items, we can use Arrays of `Elements`.
15 | - Ability to identify where the data was obtained from. (e.g. Comic Vine, Metron, Grand Comics Database, etc.)
16 | - Add `ID` elements from the Information Source to resources (Characters, Creators, etc.), so items with the same name
17 | are associated correctly if used in a Plex-like Comic Server.
18 |
19 | Since Digital Comics are just are archive files (like .zip) this new XML schema can co-exist with any existing
20 | `ComicInfo.xml` if needed for backward compatibility.
21 |
22 | ## Is the schema only for the Metron Database?
23 |
24 | No, the schema only has *Metron* in the name since almost every other format has *Comic* in the name, and the
25 | originating author hates naming projects, so he went with the simplest choice. 😄 It was designed to be used for any of
26 | the comic resources (Comic Vine, AniList, etc.)
27 |
28 | ## Where can I find the schemas?
29 |
30 | Version 1.0 of the schema is located in [schema](./schema) directory
31 |
32 | ## Is there documentation for it?
33 |
34 | Yes, there is [documentation](https://metron-project.github.io/docs/category/metroninfo) describing the elements usage
35 | and also a Matrix to help with age rating mapping.
36 |
37 | ## How can I validate my XML?
38 |
39 | It's recommended that any software that writes the XML make use of the schema to validate, so consumers of the XML
40 | document can be sure of its data. The schema use XSD 1.1, so you need to make sure your validation code uses that
41 | instead of XSD 1.0.
42 |
43 | For example to validate the XML in python:
44 |
45 | ```python
46 | from pathlib import Path
47 | from xmlschema import XMLSchema11, XMLSchemaValidationError
48 |
49 | xsd = Path("/home/user/MetronInfo.xsd")
50 | xml = Path("/home/user/MetronInfo.xml")
51 |
52 | schema = XMLSchema11(xsd)
53 | try:
54 | schema.validate(xml)
55 | except XMLSchemaValidationError as e:
56 | print(f"Failed to validate XML: {e!r}")
57 | exit(1)
58 |
59 | # Code to write / read the xml file
60 | ```
61 |
62 | ## What software currently supports it?
63 |
64 | Currently, the following software does:
65 |
66 | - [Metron-Tagger](https://github.com/Metron-Project/metron-tagger) - Commandline tool to tag comic with metadata from
67 | Metron Comic Book Database.
68 | - [Perdoo](https://github.com/Buried-In-Code/Perdoo) - Commandline tool to tag and organize comics from multiple sources
69 | (Metron, Comicvine & Marvel)
70 | - [ComicRack Community Edition](https://github.com/maforget/ComicRackCE) - Revival of the ComicRack software, supports reading metroninfo metadata.
71 | - [metroninfo_ct ](https://github.com/mizaki/metroninfo_ct) - A [Comictagger](https://github.com/comictagger/comictagger) plugin to write MetronInfo.xml metadata.
72 |
73 | If you are a developer that has added support for MetronInfo.xml to your software, please create a PR to update the
74 | README
75 | or [contact me](mailto:bpepple@metron.cloud?subject=MetronInfo%20Support&body=Please%20add%20the%20following%20software%20to%the%20README:%20).
76 |
--------------------------------------------------------------------------------
/drafts/v1.1/MetronInfo.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
--------------------------------------------------------------------------------
/drafts/v1.1/Sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 290431
5 | 12345
6 | 543
7 | 8b34f37a-0181-4f0b-8ce3-01217e9a602c
8 |
9 |
10 | DC Comics
11 | Vertigo
12 |
13 |
14 | Justice League
15 | Justice League
16 | 2
17 | Single Issue
18 | 1970
19 | 60
20 | 3
21 |
22 | Foo
23 | Hüsker Dü
24 |
25 |
26 | 1
27 |
28 | Justice League, Part One
29 | Justice League, Part Two
30 |
31 | In a universe where superheroes are strange and new, Batman has discovered a dark evil that requires him to unite the World Greatest Heroes!
32 | Nothing really to say.
33 |
34 | 3.99
35 | 1.51
36 |
37 | 2011-10-01
38 | 2011-08-31
39 | 32
40 |
41 | Super-Hero
42 | Crime
43 | Foo Bar
44 |
45 |
46 | Foo
47 | Bar
48 |
49 |
50 |
51 | Origin
52 | 1
53 |
54 |
55 | The New 52!
56 |
57 |
58 |
59 | Aquaman
60 | Batman
61 | Cyborg
62 | Deadman
63 | Barry Allen
64 | Hal Jordan
65 | Hawkman
66 | Mera
67 | Pandora
68 | Ray Palmer
69 | Superman
70 | Wonder Woman
71 |
72 |
73 | Justice League
74 | Parademons
75 |
76 |
77 |
78 | ABC
79 | Earth 25
80 |
81 |
82 | Amalgam
83 |
84 |
85 |
86 | Gotham City
87 | Metropolis
88 |
89 |
90 | 1234567890123
91 | 76194130593600111
92 |
93 | Everyone
94 |
95 | Foo Bar #001 (2002)
96 | Foo Bar #002 (2022)
97 |
98 |
99 | https://comicvine.gamespot.com/justice-league-1-justice-league-part-one/4000-290431/
100 | https://foo.bar
101 | https://bar.foo
102 |
103 |
104 |
105 | Geoff Johns
106 |
107 | Writer
108 |
109 |
110 |
111 | David Finch
112 |
113 | Cover
114 |
115 |
116 |
117 | Richard Friend
118 |
119 | Cover
120 |
121 |
122 |
123 | Jim Lee
124 |
125 | Penciller
126 | Cover
127 |
128 |
129 |
130 | Scott Williams
131 |
132 | Inker
133 | Cover
134 |
135 |
136 |
137 | Alex Sinclair
138 |
139 | Colorist
140 | Cover
141 |
142 |
143 |
144 | Pat Brosseau
145 |
146 | Letterer
147 |
148 |
149 |
150 | Rex Ogle
151 |
152 | Associate Editor
153 |
154 |
155 |
156 | Eddie Berganza
157 |
158 | Editor
159 |
160 |
161 |
162 | Dan DiDio
163 |
164 | Publisher
165 |
166 |
167 |
168 | 2023-05-31T09:00:46.300882-04:00
169 |
--------------------------------------------------------------------------------
/poetry.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
2 |
3 | [[package]]
4 | name = "colorama"
5 | version = "0.4.6"
6 | description = "Cross-platform colored terminal text."
7 | optional = false
8 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
9 | files = [
10 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
11 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
12 | ]
13 |
14 | [[package]]
15 | name = "elementpath"
16 | version = "4.6.0"
17 | description = "XPath 1.0/2.0/3.0/3.1 parsers and selectors for ElementTree and lxml"
18 | optional = false
19 | python-versions = ">=3.8"
20 | files = [
21 | {file = "elementpath-4.6.0-py3-none-any.whl", hash = "sha256:e578677f19ccc6ff374c4477c687c547ecbaf7b478d98abb951b7b4b45260a17"},
22 | {file = "elementpath-4.6.0.tar.gz", hash = "sha256:ba46bf07f66774927727ade55022b6c435fac06b2523cb3cd7689a1884d33468"},
23 | ]
24 |
25 | [package.extras]
26 | dev = ["Sphinx", "coverage", "flake8", "lxml", "lxml-stubs", "memory-profiler", "memray", "mypy", "tox", "xmlschema (>=3.3.2)"]
27 |
28 | [[package]]
29 | name = "iniconfig"
30 | version = "2.0.0"
31 | description = "brain-dead simple config-ini parsing"
32 | optional = false
33 | python-versions = ">=3.7"
34 | files = [
35 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
36 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
37 | ]
38 |
39 | [[package]]
40 | name = "packaging"
41 | version = "24.1"
42 | description = "Core utilities for Python packages"
43 | optional = false
44 | python-versions = ">=3.8"
45 | files = [
46 | {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
47 | {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
48 | ]
49 |
50 | [[package]]
51 | name = "pluggy"
52 | version = "1.5.0"
53 | description = "plugin and hook calling mechanisms for python"
54 | optional = false
55 | python-versions = ">=3.8"
56 | files = [
57 | {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
58 | {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
59 | ]
60 |
61 | [package.extras]
62 | dev = ["pre-commit", "tox"]
63 | testing = ["pytest", "pytest-benchmark"]
64 |
65 | [[package]]
66 | name = "pytest"
67 | version = "8.3.3"
68 | description = "pytest: simple powerful testing with Python"
69 | optional = false
70 | python-versions = ">=3.8"
71 | files = [
72 | {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"},
73 | {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"},
74 | ]
75 |
76 | [package.dependencies]
77 | colorama = {version = "*", markers = "sys_platform == \"win32\""}
78 | iniconfig = "*"
79 | packaging = "*"
80 | pluggy = ">=1.5,<2"
81 |
82 | [package.extras]
83 | dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
84 |
85 | [[package]]
86 | name = "ruff"
87 | version = "0.7.1"
88 | description = "An extremely fast Python linter and code formatter, written in Rust."
89 | optional = false
90 | python-versions = ">=3.7"
91 | files = [
92 | {file = "ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89"},
93 | {file = "ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35"},
94 | {file = "ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99"},
95 | {file = "ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca"},
96 | {file = "ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250"},
97 | {file = "ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c"},
98 | {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565"},
99 | {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7"},
100 | {file = "ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a"},
101 | {file = "ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad"},
102 | {file = "ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112"},
103 | {file = "ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378"},
104 | {file = "ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8"},
105 | {file = "ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd"},
106 | {file = "ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9"},
107 | {file = "ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307"},
108 | {file = "ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37"},
109 | {file = "ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4"},
110 | ]
111 |
112 | [[package]]
113 | name = "xmlschema"
114 | version = "3.4.2"
115 | description = "An XML Schema validator and decoder"
116 | optional = false
117 | python-versions = ">=3.8"
118 | files = [
119 | {file = "xmlschema-3.4.2-py3-none-any.whl", hash = "sha256:c6b4de5f8aadeb45e74229f09a2129342b446456efc5e5a27388050afdfedec8"},
120 | {file = "xmlschema-3.4.2.tar.gz", hash = "sha256:d35023ea504ea46127302d1297b046d023b96fec5fe4b4b690534ea85b5e9bf8"},
121 | ]
122 |
123 | [package.dependencies]
124 | elementpath = ">=4.4.0,<5.0.0"
125 |
126 | [package.extras]
127 | codegen = ["elementpath (>=4.4.0,<5.0.0)", "jinja2"]
128 | dev = ["Sphinx", "coverage", "elementpath (>=4.4.0,<5.0.0)", "flake8", "jinja2", "lxml", "lxml-stubs", "memory-profiler", "mypy", "sphinx-rtd-theme", "tox"]
129 | docs = ["Sphinx", "elementpath (>=4.4.0,<5.0.0)", "jinja2", "sphinx-rtd-theme"]
130 |
131 | [metadata]
132 | lock-version = "2.0"
133 | python-versions = "^3.12"
134 | content-hash = "4a6a53745c4ade616e0419283d9da59035204046ff1c0f66e3f54b96c848b018"
135 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "metroninfo"
3 | version = "0.1.0"
4 | description = ""
5 | authors = ["Brian Pepple "]
6 | readme = "README.md"
7 | package-mode = false
8 |
9 | [tool.poetry.dependencies]
10 | python = "^3.12"
11 |
12 | [tool.poetry.group.dev.dependencies]
13 | xmlschema = "^3.4.2"
14 | pytest = "^8.3.3"
15 | ruff = "^0.7.1"
16 |
17 | [build-system]
18 | requires = ["poetry-core"]
19 | build-backend = "poetry.core.masonry.api"
20 |
21 | [tool.pytest.ini_options]
22 | testpaths = "tests/test*"
23 |
--------------------------------------------------------------------------------
/schema/v1.0/MetronInfo.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
--------------------------------------------------------------------------------
/schema/v1.0/Sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 290431
5 | 12345
6 | 543
7 | 8b34f37a-0181-4f0b-8ce3-01217e9a602c
8 |
9 |
10 | DC Comics
11 | Vertigo
12 |
13 |
14 | Justice League
15 | Justice League
16 | 2
17 | Single Issue
18 | 1970
19 | 60
20 | 3
21 |
22 | Foo
23 | Hüsker Dü
24 |
25 |
26 | 1
27 |
28 | Justice League, Part One
29 | Justice League, Part Two
30 |
31 | In a universe where superheroes are strange and new, Batman has discovered a dark evil that requires him to unite the World Greatest Heroes!
32 | Nothing really to say.
33 |
34 | 3.99
35 | 1.51
36 |
37 | 2011-10-01
38 | 2011-08-31
39 | 32
40 |
41 | Super-Hero
42 | Crime
43 | Foo Bar
44 |
45 |
46 | Foo
47 | Bar
48 |
49 |
50 |
51 | Origin
52 | 1
53 |
54 |
55 | The New 52!
56 |
57 |
58 |
59 | Aquaman
60 | Batman
61 | Cyborg
62 | Deadman
63 | Barry Allen
64 | Hal Jordan
65 | Hawkman
66 | Mera
67 | Pandora
68 | Ray Palmer
69 | Superman
70 | Wonder Woman
71 |
72 |
73 | Justice League
74 | Parademons
75 |
76 |
77 |
78 | ABC
79 | Earth 25
80 |
81 |
82 | Amalgam
83 |
84 |
85 |
86 | Gotham City
87 | Metropolis
88 |
89 |
90 | 1234567890123
91 | 76194130593600111
92 |
93 | Everyone
94 |
95 | Foo Bar #001 (2002)
96 | Foo Bar #002 (2022)
97 |
98 |
99 | https://comicvine.gamespot.com/justice-league-1-justice-league-part-one/4000-290431/
100 | https://foo.bar
101 | https://bar.foo
102 |
103 |
104 |
105 | Geoff Johns
106 |
107 | Writer
108 |
109 |
110 |
111 | David Finch
112 |
113 | Cover
114 |
115 |
116 |
117 | Richard Friend
118 |
119 | Cover
120 |
121 |
122 |
123 | Jim Lee
124 |
125 | Penciller
126 | Cover
127 |
128 |
129 |
130 | Scott Williams
131 |
132 | Inker
133 | Cover
134 |
135 |
136 |
137 | Alex Sinclair
138 |
139 | Colorist
140 | Cover
141 |
142 |
143 |
144 | Pat Brosseau
145 |
146 | Letterer
147 |
148 |
149 |
150 | Rex Ogle
151 |
152 | Associate Editor
153 |
154 |
155 |
156 | Eddie Berganza
157 |
158 | Editor
159 |
160 |
161 |
162 | Dan DiDio
163 |
164 | Publisher
165 |
166 |
167 |
168 | 2023-05-31T09:00:46.300882-04:00
169 |
--------------------------------------------------------------------------------
/tests/test.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import pytest
4 | from xmlschema import XMLSchema11, XMLSchemaValidationError
5 |
6 | TEST_V10_XSD = Path(__file__).parent.parent / "schema" / "v1.0" / "MetronInfo.xsd"
7 | TEST_V11_XSD = Path(__file__).parent.parent / "drafts" / "v1.1" / "MetronInfo.xsd"
8 | TEST_FILES_PATH = Path(__file__).parent / "test_files" / "v1.0"
9 |
10 |
11 | @pytest.mark.parametrize(
12 | ("xsd", "xml"),
13 | [
14 | (TEST_V10_XSD, TEST_FILES_PATH / "valid.xml"),
15 | (
16 | TEST_V10_XSD,
17 | ''
18 | "Foo0",
19 | ),
20 | (
21 | TEST_V10_XSD,
22 | ''
23 | "Foo0",
24 | ),
25 | (TEST_V11_XSD, TEST_FILES_PATH / "valid.xml"),
26 | (
27 | TEST_V10_XSD,
28 | ''
29 | "Foo0",
30 | ),
31 | (
32 | TEST_V10_XSD,
33 | ''
34 | "Foo0",
35 | ),
36 | ],
37 | ids=["valid_xml", "zero_page_count", "volume_zero", "v11_valid_xml", "v11_zero_page_count", "v11_volume_zero"],
38 | )
39 | def test_valid(xsd: Path, xml: Path | str) -> None:
40 | schema = XMLSchema11(xsd)
41 | schema.validate(xml)
42 |
43 |
44 | @pytest.mark.parametrize(
45 | ("xsd", "xml"),
46 | [
47 | (TEST_V10_XSD, TEST_FILES_PATH / "dup_primary_attr.xml"),
48 | (
49 | TEST_V10_XSD,
50 | ''
51 | "Foo-1",
52 | ),
53 | (
54 | TEST_V10_XSD,
55 | ''
56 | "Foo-1",
57 | ),
58 | (TEST_V11_XSD, TEST_FILES_PATH / "dup_primary_attr.xml"),
59 | (
60 | TEST_V11_XSD,
61 | ''
62 | "Foo-1",
63 | ),
64 | (
65 | TEST_V11_XSD,
66 | ''
67 | "Foo-1",
68 | ),
69 | ],
70 | ids=["dup_primary_attr_xml", "negative_page_count", "negative_volume", "v11_dup_primary_attr_xml", "v11_negative_page_count", "v11_negative_volume" ],
71 | )
72 | def test_invalid(xsd: Path, xml: Path | str) -> None:
73 | schema = XMLSchema11(xsd)
74 | with pytest.raises(XMLSchemaValidationError):
75 | schema.validate(xml)
76 |
--------------------------------------------------------------------------------
/tests/test_files/v1.0/dup_primary_attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 290431
5 | 12345
6 | 543
7 |
8 |
9 | DC Comics
10 | Vertigo
11 |
12 |
13 | Justice League
14 | Justice League
15 | 2
16 | Single Issue
17 | 1970
18 |
19 | Foo
20 | Hüsker Dü
21 |
22 |
23 | 1
24 |
25 | Justice League, Part One
26 | Justice League, Part Two
27 |
28 | In a universe where superheroes are strange and new, Batman has discovered a dark evil that requires him to unite the World Greatest Heroes!
29 | Nothing really to say.
30 |
31 | 3.99
32 | 1.51
33 |
34 | 2011-10-01
35 | 2011-08-31
36 | 32
37 |
38 | Super-Hero
39 | Crime
40 | Foo Bar
41 |
42 |
43 | Foo
44 | Bar
45 |
46 |
47 |
48 | Origin
49 | 1
50 |
51 |
52 | The New 52!
53 |
54 |
55 |
56 | Aquaman
57 | Batman
58 | Cyborg
59 | Deadman
60 | Barry Allen
61 | Hal Jordan
62 | Hawkman
63 | Mera
64 | Pandora
65 | Ray Palmer
66 | Superman
67 | Wonder Woman
68 |
69 |
70 | Justice League
71 | Parademons
72 |
73 |
74 |
75 | ABC
76 | Earth 25
77 |
78 |
79 | Amalgam
80 |
81 |
82 |
83 | Gotham City
84 | Metropolis
85 |
86 |
87 | 1234567890123
88 | 76194130593600111
89 |
90 | Everyone
91 |
92 | Foo Bar #001 (2002)
93 | Foo Bar #002 (2022)
94 |
95 |
96 | https://comicvine.gamespot.com/justice-league-1-justice-league-part-one/4000-290431/
97 | https://foo.bar
98 | https://bar.foo
99 |
100 |
101 |
102 | Geoff Johns
103 |
104 | Writer
105 |
106 |
107 |
108 | David Finch
109 |
110 | Cover
111 |
112 |
113 |
114 | Richard Friend
115 |
116 | Cover
117 |
118 |
119 |
120 | Jim Lee
121 |
122 | Penciller
123 | Cover
124 |
125 |
126 |
127 | Scott Williams
128 |
129 | Inker
130 | Cover
131 |
132 |
133 |
134 | Alex Sinclair
135 |
136 | Colorist
137 | Cover
138 |
139 |
140 |
141 | Pat Brosseau
142 |
143 | Letterer
144 |
145 |
146 |
147 | Rex Ogle
148 |
149 | Associate Editor
150 |
151 |
152 |
153 | Eddie Berganza
154 |
155 | Editor
156 |
157 |
158 |
159 | Dan DiDio
160 |
161 | Publisher
162 |
163 |
164 |
165 | 2023-05-31T09:00:46.300882-04:00
166 |
--------------------------------------------------------------------------------
/tests/test_files/v1.0/valid.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 290431
5 | 12345
6 | 543
7 |
8 |
9 | DC Comics
10 | Vertigo
11 |
12 |
13 | Justice League
14 | Justice League
15 | 2
16 | Single Issue
17 | 1970
18 | 60
19 | 3
20 |
21 | Foo
22 | Hüsker Dü
23 |
24 |
25 | 1
26 |
27 | Justice League, Part One
28 | Justice League, Part Two
29 |
30 | In a universe where superheroes are strange and new, Batman has discovered a dark evil that requires him to unite the World Greatest Heroes!
31 | Nothing really to say.
32 |
33 | 3.99
34 | 1.51
35 |
36 | 2011-10-01
37 | 2011-08-31
38 | 32
39 |
40 | Super-Hero
41 | Crime
42 | Foo Bar
43 |
44 |
45 | Foo
46 | Bar
47 |
48 |
49 |
50 | Origin
51 | 1
52 |
53 |
54 | The New 52!
55 |
56 |
57 |
58 | Aquaman
59 | Batman
60 | Cyborg
61 | Deadman
62 | Barry Allen
63 | Hal Jordan
64 | Hawkman
65 | Mera
66 | Pandora
67 | Ray Palmer
68 | Superman
69 | Wonder Woman
70 |
71 |
72 | Justice League
73 | Parademons
74 |
75 |
76 |
77 | ABC
78 | Earth 25
79 |
80 |
81 | Amalgam
82 |
83 |
84 |
85 | Gotham City
86 | Metropolis
87 |
88 |
89 | 1234567890123
90 | 76194130593600111
91 |
92 | Everyone
93 |
94 | Foo Bar #001 (2002)
95 | Foo Bar #002 (2022)
96 |
97 |
98 | https://comicvine.gamespot.com/justice-league-1-justice-league-part-one/4000-290431/
99 | https://foo.bar
100 | https://bar.foo
101 |
102 |
103 |
104 | Geoff Johns
105 |
106 | Writer
107 |
108 |
109 |
110 | David Finch
111 |
112 | Cover
113 |
114 |
115 |
116 | Richard Friend
117 |
118 | Cover
119 |
120 |
121 |
122 | Jim Lee
123 |
124 | Penciller
125 | Cover
126 |
127 |
128 |
129 | Scott Williams
130 |
131 | Inker
132 | Cover
133 |
134 |
135 |
136 | Alex Sinclair
137 |
138 | Colorist
139 | Cover
140 |
141 |
142 |
143 | Pat Brosseau
144 |
145 | Letterer
146 |
147 |
148 |
149 | Rex Ogle
150 |
151 | Associate Editor
152 |
153 |
154 |
155 | Eddie Berganza
156 |
157 | Editor
158 |
159 |
160 |
161 | Dan DiDio
162 |
163 | Publisher
164 |
165 |
166 |
167 | 2023-05-31T09:00:46.300882-04:00
168 |
--------------------------------------------------------------------------------