c2pa-python 0.8.3__tar.gz → 0.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. c2pa_python-0.10.0/MANIFEST.in +6 -0
  2. c2pa_python-0.10.0/PKG-INFO +156 -0
  3. c2pa_python-0.10.0/README.md +133 -0
  4. c2pa_python-0.10.0/pyproject.toml +37 -0
  5. c2pa_python-0.10.0/requirements.txt +2 -0
  6. c2pa_python-0.10.0/scripts/download_artifacts.py +159 -0
  7. c2pa_python-0.10.0/setup.cfg +4 -0
  8. c2pa_python-0.10.0/setup.py +229 -0
  9. c2pa_python-0.10.0/src/c2pa/__init__.py +30 -0
  10. c2pa_python-0.10.0/src/c2pa/build.py +116 -0
  11. c2pa_python-0.10.0/src/c2pa/c2pa.py +2002 -0
  12. c2pa_python-0.10.0/src/c2pa/lib.py +258 -0
  13. c2pa_python-0.10.0/src/c2pa_python.egg-info/PKG-INFO +156 -0
  14. c2pa_python-0.10.0/src/c2pa_python.egg-info/SOURCES.txt +20 -0
  15. c2pa_python-0.10.0/src/c2pa_python.egg-info/dependency_links.txt +1 -0
  16. c2pa_python-0.10.0/src/c2pa_python.egg-info/entry_points.txt +2 -0
  17. c2pa_python-0.10.0/src/c2pa_python.egg-info/requires.txt +6 -0
  18. c2pa_python-0.10.0/src/c2pa_python.egg-info/top_level.txt +1 -0
  19. c2pa_python-0.10.0/tests/test_unit_tests.py +952 -0
  20. c2pa_python-0.10.0/tests/test_unit_tests_threaded.py +2249 -0
  21. c2pa_python-0.8.3/.github/workflows/build.yml +0 -393
  22. c2pa_python-0.8.3/.github/workflows/closing_ticket.yml +0 -15
  23. c2pa_python-0.8.3/.github/workflows/labeling_ticket_done.yml +0 -17
  24. c2pa_python-0.8.3/.github/workflows/labeling_ticket_todo.yml +0 -17
  25. c2pa_python-0.8.3/.github/workflows/reopening_ticket.yml +0 -15
  26. c2pa_python-0.8.3/.github/workflows/upload-test.yml +0 -114
  27. c2pa_python-0.8.3/.gitignore +0 -28
  28. c2pa_python-0.8.3/.vscode/settings.json +0 -6
  29. c2pa_python-0.8.3/CODE_OF_CONDUCT.md +0 -74
  30. c2pa_python-0.8.3/CONTRIBUTING.md +0 -104
  31. c2pa_python-0.8.3/Cargo.lock +0 -3425
  32. c2pa_python-0.8.3/Cargo.toml +0 -22
  33. c2pa_python-0.8.3/Makefile +0 -22
  34. c2pa_python-0.8.3/PKG-INFO +0 -82
  35. c2pa_python-0.8.3/README.md +0 -59
  36. c2pa_python-0.8.3/SECURITY.md +0 -22
  37. c2pa_python-0.8.3/build.rs +0 -4
  38. c2pa_python-0.8.3/c2pa/__init__.py +0 -45
  39. c2pa_python-0.8.3/c2pa/c2pa_api/__init__.py +0 -1
  40. c2pa_python-0.8.3/c2pa/c2pa_api/c2pa_api.py +0 -265
  41. c2pa_python-0.8.3/deny.toml +0 -56
  42. c2pa_python-0.8.3/docs/project-contributions.md +0 -105
  43. c2pa_python-0.8.3/docs/release-notes.md +0 -56
  44. c2pa_python-0.8.3/docs/usage.md +0 -218
  45. c2pa_python-0.8.3/pyproject.toml +0 -33
  46. c2pa_python-0.8.3/requirements.txt +0 -4
  47. c2pa_python-0.8.3/src/c2pa.udl +0 -113
  48. c2pa_python-0.8.3/src/callback_signer.rs +0 -108
  49. c2pa_python-0.8.3/src/error.rs +0 -126
  50. c2pa_python-0.8.3/src/lib.rs +0 -254
  51. c2pa_python-0.8.3/src/streams.rs +0 -164
  52. c2pa_python-0.8.3/src/test_stream.rs +0 -85
  53. c2pa_python-0.8.3/tests/benchmark.py +0 -71
  54. c2pa_python-0.8.3/tests/fixtures/A.jpg +0 -0
  55. c2pa_python-0.8.3/tests/fixtures/A_thumbnail.jpg +0 -0
  56. c2pa_python-0.8.3/tests/fixtures/C.jpg +0 -0
  57. c2pa_python-0.8.3/tests/fixtures/cloud.jpg +0 -0
  58. c2pa_python-0.8.3/tests/fixtures/es256_certs.pem +0 -32
  59. c2pa_python-0.8.3/tests/fixtures/es256_private.key +0 -5
  60. c2pa_python-0.8.3/tests/fixtures/ps256.pem +0 -53
  61. c2pa_python-0.8.3/tests/fixtures/ps256.pub +0 -113
  62. c2pa_python-0.8.3/tests/fixtures/settings.toml +0 -230
  63. c2pa_python-0.8.3/tests/test_api.py +0 -192
  64. c2pa_python-0.8.3/tests/test_unit_tests.py +0 -137
  65. c2pa_python-0.8.3/tests/training.py +0 -124
  66. {c2pa_python-0.8.3 → c2pa_python-0.10.0}/LICENSE-APACHE +0 -0
  67. {c2pa_python-0.8.3 → c2pa_python-0.10.0}/LICENSE-MIT +0 -0
@@ -0,0 +1,6 @@
1
+ include LICENSE-MIT
2
+ include LICENSE-APACHE
3
+ include README.md
4
+ include requirements.txt
5
+ include scripts/download_artifacts.py
6
+ recursive-include src/c2pa *.py
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: c2pa-python
3
+ Version: 0.10.0
4
+ Summary: Python bindings for the C2PA Content Authenticity Initiative (CAI) library
5
+ Author-email: Gavin Peacock <gvnpeacock@adobe.com>, Tania Mathern <mathern@adobe.com>
6
+ Maintainer-email: Gavin Peacock <gpeacock@adobe.com>
7
+ License: MIT OR Apache-2.0
8
+ Project-URL: homepage, https://contentauthenticity.org
9
+ Project-URL: repository, https://github.com/contentauth/c2pa-python
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: MacOS
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: wheel>=0.41.2
17
+ Requires-Dist: setuptools>=68.0.0
18
+ Requires-Dist: toml>=0.10.2
19
+ Requires-Dist: pytest>=7.4.0
20
+ Requires-Dist: cryptography>=41.0.0
21
+ Requires-Dist: requests>=2.0.0
22
+ Dynamic: requires-python
23
+
24
+ # Python API for C2PA
25
+
26
+ This project provides a Python API for working with [C2PA](https://c2pa.org/) (Coalition for Content Provenance and Authenticity) manifests. It includes functionality for creating, signing, and verifying C2PA manifests, as well as working with assets and assertions.
27
+
28
+ ## Features
29
+
30
+ - Create and sign C2PA manifests using various signing algorithms.
31
+ - Verify C2PA manifests and extract metadata.
32
+ - Add assertions and ingredients to assets.
33
+ - Examples and unit tests to demonstrate usage.
34
+
35
+ ## Project Structure
36
+
37
+ ```bash
38
+ .
39
+ ├── .github/ # GitHub configuration files
40
+ ├── artifacts/ # Platform-specific libraries for building (per subfolder)
41
+ │ └── your_target_platform/ # Platform-specific artifacts
42
+ ├── docs/ # Project documentation
43
+ ├── examples/ # Example scripts demonstrating usage
44
+ ├── scripts/ # Utility scripts (eg. artifacts download)
45
+ ├── src/ # Source code
46
+ │ └── c2pa/ # Main package directory
47
+ │ └── libs/ # Platform-specific libraries
48
+ ├── tests/ # Unit tests and benchmarks
49
+ ├── .gitignore # Git ignore rules
50
+ ├── Makefile # Build and development commands
51
+ ├── pyproject.toml # Python project configuration
52
+ ├── requirements.txt # Python dependencies
53
+ ├── requirements-dev.txt # Development dependencies
54
+ └── setup.py # Package setup script
55
+ ```
56
+
57
+ ## Package installation
58
+
59
+ The c2pa-python package is published to PyPI. You can install it from there by running:
60
+
61
+ ```bash
62
+ pip install c2pa-python
63
+ ```
64
+
65
+ To use the module in your Python code, import like this:
66
+
67
+ ```python
68
+ import c2pa
69
+ ```
70
+
71
+ ## Examples
72
+
73
+ ### Adding a "Do Not Train" Assertion
74
+
75
+ The `examples/training.py` script demonstrates how to add a "Do Not Train" assertion to an asset and verify it.
76
+
77
+ ### Signing and Verifying Assets
78
+
79
+ The `examples/sign.py` script shows how to sign an asset with a C2PA manifest and verify it.
80
+
81
+ ## Development Setup
82
+
83
+ 1. Create and activate a virtual environment with native dependencies:
84
+
85
+ ```bash
86
+ # Create virtual environment
87
+ python -m venv .venv
88
+
89
+ # Activate virtual environment
90
+ # On Windows:
91
+ .venv\Scripts\activate
92
+ # On macOS/Linux:
93
+ source .venv/bin/activate
94
+
95
+ # load project dependencies
96
+ pip install -r requirements.txt
97
+ pip install -r requirements-dev.txt
98
+
99
+ # download library artifacts for the current version you want, eg v0.55.0
100
+ python scripts/download_artifacts.py c2pa-v0.55.0
101
+ ```
102
+
103
+ 2. Install the package in development mode:
104
+
105
+ ```bash
106
+ pip install -e .
107
+ ```
108
+
109
+ This will:
110
+
111
+ - Copy the appropriate libraries for your platform from `artifacts/` to `src/c2pa/libs/`
112
+ - Install the package in development mode, allowing you to make changes to the Python code without reinstalling
113
+
114
+ ## Building Wheels
115
+
116
+ To build wheels for all platforms that have libraries in the `artifacts/` directory:
117
+
118
+ ```bash
119
+ python setup.py bdist_wheel
120
+ ```
121
+
122
+ You can use `twine` to verify the wheels have correct metadata:
123
+
124
+ ```bash
125
+ twine check dist/*
126
+ ```
127
+
128
+ This will create platform-specific wheels in the `dist/` directory.
129
+
130
+ ## Running Tests
131
+
132
+ Run the tests:
133
+
134
+ ```bash
135
+ make test
136
+ ```
137
+
138
+ Alternatively, install pytest (if not already installed):
139
+
140
+ ```bash
141
+ pip install pytest
142
+ ```
143
+
144
+ And run:
145
+
146
+ ```bash
147
+ pytest
148
+ ```
149
+
150
+ ## Contributing
151
+
152
+ Contributions are welcome! Please fork the repository and submit a pull request.
153
+
154
+ ## License
155
+
156
+ This project is licensed under the Apache License 2.0 or the MIT License. See the LICENSE-MIT and LICENSE-APACHE files for details.
@@ -0,0 +1,133 @@
1
+ # Python API for C2PA
2
+
3
+ This project provides a Python API for working with [C2PA](https://c2pa.org/) (Coalition for Content Provenance and Authenticity) manifests. It includes functionality for creating, signing, and verifying C2PA manifests, as well as working with assets and assertions.
4
+
5
+ ## Features
6
+
7
+ - Create and sign C2PA manifests using various signing algorithms.
8
+ - Verify C2PA manifests and extract metadata.
9
+ - Add assertions and ingredients to assets.
10
+ - Examples and unit tests to demonstrate usage.
11
+
12
+ ## Project Structure
13
+
14
+ ```bash
15
+ .
16
+ ├── .github/ # GitHub configuration files
17
+ ├── artifacts/ # Platform-specific libraries for building (per subfolder)
18
+ │ └── your_target_platform/ # Platform-specific artifacts
19
+ ├── docs/ # Project documentation
20
+ ├── examples/ # Example scripts demonstrating usage
21
+ ├── scripts/ # Utility scripts (eg. artifacts download)
22
+ ├── src/ # Source code
23
+ │ └── c2pa/ # Main package directory
24
+ │ └── libs/ # Platform-specific libraries
25
+ ├── tests/ # Unit tests and benchmarks
26
+ ├── .gitignore # Git ignore rules
27
+ ├── Makefile # Build and development commands
28
+ ├── pyproject.toml # Python project configuration
29
+ ├── requirements.txt # Python dependencies
30
+ ├── requirements-dev.txt # Development dependencies
31
+ └── setup.py # Package setup script
32
+ ```
33
+
34
+ ## Package installation
35
+
36
+ The c2pa-python package is published to PyPI. You can install it from there by running:
37
+
38
+ ```bash
39
+ pip install c2pa-python
40
+ ```
41
+
42
+ To use the module in your Python code, import like this:
43
+
44
+ ```python
45
+ import c2pa
46
+ ```
47
+
48
+ ## Examples
49
+
50
+ ### Adding a "Do Not Train" Assertion
51
+
52
+ The `examples/training.py` script demonstrates how to add a "Do Not Train" assertion to an asset and verify it.
53
+
54
+ ### Signing and Verifying Assets
55
+
56
+ The `examples/sign.py` script shows how to sign an asset with a C2PA manifest and verify it.
57
+
58
+ ## Development Setup
59
+
60
+ 1. Create and activate a virtual environment with native dependencies:
61
+
62
+ ```bash
63
+ # Create virtual environment
64
+ python -m venv .venv
65
+
66
+ # Activate virtual environment
67
+ # On Windows:
68
+ .venv\Scripts\activate
69
+ # On macOS/Linux:
70
+ source .venv/bin/activate
71
+
72
+ # load project dependencies
73
+ pip install -r requirements.txt
74
+ pip install -r requirements-dev.txt
75
+
76
+ # download library artifacts for the current version you want, eg v0.55.0
77
+ python scripts/download_artifacts.py c2pa-v0.55.0
78
+ ```
79
+
80
+ 2. Install the package in development mode:
81
+
82
+ ```bash
83
+ pip install -e .
84
+ ```
85
+
86
+ This will:
87
+
88
+ - Copy the appropriate libraries for your platform from `artifacts/` to `src/c2pa/libs/`
89
+ - Install the package in development mode, allowing you to make changes to the Python code without reinstalling
90
+
91
+ ## Building Wheels
92
+
93
+ To build wheels for all platforms that have libraries in the `artifacts/` directory:
94
+
95
+ ```bash
96
+ python setup.py bdist_wheel
97
+ ```
98
+
99
+ You can use `twine` to verify the wheels have correct metadata:
100
+
101
+ ```bash
102
+ twine check dist/*
103
+ ```
104
+
105
+ This will create platform-specific wheels in the `dist/` directory.
106
+
107
+ ## Running Tests
108
+
109
+ Run the tests:
110
+
111
+ ```bash
112
+ make test
113
+ ```
114
+
115
+ Alternatively, install pytest (if not already installed):
116
+
117
+ ```bash
118
+ pip install pytest
119
+ ```
120
+
121
+ And run:
122
+
123
+ ```bash
124
+ pytest
125
+ ```
126
+
127
+ ## Contributing
128
+
129
+ Contributions are welcome! Please fork the repository and submit a pull request.
130
+
131
+ ## License
132
+
133
+ This project is licensed under the Apache License 2.0 or the MIT License. See the LICENSE-MIT and LICENSE-APACHE files for details.
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0.0", "wheel", "toml>=0.10.2"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "c2pa-python"
7
+ version = "0.10.0"
8
+ requires-python = ">=3.10"
9
+ description = "Python bindings for the C2PA Content Authenticity Initiative (CAI) library"
10
+ readme = { file = "README.md", content-type = "text/markdown" }
11
+ license = { text = "MIT OR Apache-2.0" }
12
+ authors = [{ name = "Gavin Peacock", email = "gvnpeacock@adobe.com" }, { name = "Tania Mathern", email = "mathern@adobe.com" }]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "Operating System :: MacOS",
16
+ "Operating System :: POSIX :: Linux",
17
+ "Operating System :: Microsoft :: Windows"
18
+ ]
19
+ maintainers = [
20
+ {name = "Gavin Peacock", email = "gpeacock@adobe.com"}
21
+ ]
22
+ urls = {homepage = "https://contentauthenticity.org", repository = "https://github.com/contentauth/c2pa-python"}
23
+ dependencies = [
24
+ "wheel>=0.41.2",
25
+ "setuptools>=68.0.0",
26
+ "toml>=0.10.2",
27
+ "pytest>=7.4.0",
28
+ "cryptography>=41.0.0",
29
+ "requests>=2.0.0"
30
+ ]
31
+
32
+ [project.scripts]
33
+ download-artifacts = "c2pa.build:download_artifacts"
34
+
35
+ # Workaround to prevent setuptools from automatically including invalid metadata
36
+ [tool.setuptools]
37
+ license-files = []
@@ -0,0 +1,2 @@
1
+ # only used in the training example
2
+ cryptography>=45.0.3
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env python3
2
+ import os
3
+ import sys
4
+ import requests
5
+ from pathlib import Path
6
+ import zipfile
7
+ import io
8
+ import shutil
9
+ import platform
10
+ import subprocess
11
+
12
+ # Constants
13
+ REPO_OWNER = "contentauth"
14
+ REPO_NAME = "c2pa-rs"
15
+ GITHUB_API_BASE = "https://api.github.com"
16
+ SCRIPTS_ARTIFACTS_DIR = Path("scripts/artifacts")
17
+ ROOT_ARTIFACTS_DIR = Path("artifacts")
18
+
19
+ def detect_os():
20
+ """Detect the operating system and return the corresponding platform identifier."""
21
+ system = platform.system().lower()
22
+ if system == "darwin":
23
+ return "apple-darwin"
24
+ elif system == "linux":
25
+ return "unknown-linux-gnu"
26
+ elif system == "windows":
27
+ return "pc-windows-msvc"
28
+ else:
29
+ raise ValueError(f"Unsupported operating system: {system}")
30
+
31
+ def detect_arch():
32
+ """Detect the CPU architecture and return the corresponding identifier."""
33
+ machine = platform.machine().lower()
34
+
35
+ # Handle common architecture names
36
+ if machine in ["x86_64", "amd64"]:
37
+ return "x86_64"
38
+ elif machine in ["arm64", "aarch64"]:
39
+ return "aarch64"
40
+ else:
41
+ raise ValueError(f"Unsupported CPU architecture: {machine}")
42
+
43
+ def get_platform_identifier():
44
+ """Get the full platform identifier (arch-os) for the current system,
45
+ matching the identifiers used by the Github publisher.
46
+ Returns one of:
47
+ - universal-apple-darwin (for Mac)
48
+ - x86_64-pc-windows-msvc (for Windows 64-bit)
49
+ - x86_64-unknown-linux-gnu (for Linux 64-bit)
50
+ """
51
+ system = platform.system().lower()
52
+
53
+ if system == "darwin":
54
+ return "universal-apple-darwin"
55
+ elif system == "windows":
56
+ return "x86_64-pc-windows-msvc"
57
+ elif system == "linux":
58
+ return "x86_64-unknown-linux-gnu"
59
+ else:
60
+ raise ValueError(f"Unsupported operating system: {system}")
61
+
62
+ def get_release_by_tag(tag):
63
+ """Get release information for a specific tag from GitHub."""
64
+ url = f"{GITHUB_API_BASE}/repos/{REPO_OWNER}/{REPO_NAME}/releases/tags/{tag}"
65
+ print(f"Fetching release information from {url}...")
66
+ headers = {}
67
+ if 'GITHUB_TOKEN' in os.environ:
68
+ headers['Authorization'] = f"token {os.environ['GITHUB_TOKEN']}"
69
+ response = requests.get(url, headers=headers)
70
+ response.raise_for_status()
71
+ return response.json()
72
+
73
+ def download_and_extract_libs(url, platform_name):
74
+ """Download a zip artifact and extract only the libs folder."""
75
+ print(f"Downloading artifact for {platform_name}...")
76
+ platform_dir = SCRIPTS_ARTIFACTS_DIR / platform_name
77
+ platform_dir.mkdir(parents=True, exist_ok=True)
78
+
79
+ headers = {}
80
+ if 'GITHUB_TOKEN' in os.environ:
81
+ headers['Authorization'] = f"token {os.environ['GITHUB_TOKEN']}"
82
+ response = requests.get(url, headers=headers)
83
+ response.raise_for_status()
84
+
85
+ with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref:
86
+ # Extract only files inside the libs/ directory
87
+ for member in zip_ref.namelist():
88
+ print(f" Processing zip member: {member}")
89
+ if member.startswith("lib/") and not member.endswith("/"):
90
+ print(f" Processing lib file from downloadedzip: {member}")
91
+ target_path = platform_dir / os.path.relpath(member, "lib")
92
+ print(f" Moving file to target path: {target_path}")
93
+ target_path.parent.mkdir(parents=True, exist_ok=True)
94
+ with zip_ref.open(member) as source, open(target_path, "wb") as target:
95
+ target.write(source.read())
96
+
97
+ print(f"Done downloading and extracting libraries for {platform_name}")
98
+
99
+ def copy_artifacts_to_root():
100
+ """Copy the artifacts folder from scripts/artifacts to the root of the repository."""
101
+ if not SCRIPTS_ARTIFACTS_DIR.exists():
102
+ print("No artifacts found in scripts/artifacts")
103
+ return
104
+
105
+ print("Copying artifacts from scripts/artifacts to root...")
106
+ if ROOT_ARTIFACTS_DIR.exists():
107
+ shutil.rmtree(ROOT_ARTIFACTS_DIR)
108
+ shutil.copytree(SCRIPTS_ARTIFACTS_DIR, ROOT_ARTIFACTS_DIR)
109
+ print("Done copying artifacts")
110
+
111
+ def main():
112
+ if len(sys.argv) < 2:
113
+ print("Usage: python download_artifacts.py <release_tag>")
114
+ print("Example: python download_artifacts.py c2pa-v0.49.5")
115
+ sys.exit(1)
116
+
117
+ release_tag = sys.argv[1]
118
+ try:
119
+ SCRIPTS_ARTIFACTS_DIR.mkdir(exist_ok=True)
120
+ print(f"Fetching release information for tag {release_tag}...")
121
+ release = get_release_by_tag(release_tag)
122
+ print(f"Found release: {release['tag_name']} \n")
123
+
124
+ # Get the platform identifier for the current system
125
+ env_platform = os.environ.get("C2PA_LIBS_PLATFORM")
126
+ if env_platform:
127
+ print(f"Using platform from environment variable C2PA_LIBS_PLATFORM: {env_platform}")
128
+ platform_id = env_platform or get_platform_identifier()
129
+ platform_source = "environment variable" if env_platform else "auto-detection"
130
+ print(f"Target platform: {platform_id} (set through{platform_source})")
131
+
132
+ # Construct the expected asset name
133
+ expected_asset_name = f"{release_tag}-{platform_id}.zip"
134
+ print(f"Looking for asset: {expected_asset_name}")
135
+
136
+ # Find the matching asset in the release
137
+ matching_asset = None
138
+ for asset in release['assets']:
139
+ if asset['name'] == expected_asset_name:
140
+ matching_asset = asset
141
+ break
142
+
143
+ if matching_asset:
144
+ print(f"Found matching asset: {matching_asset['name']}")
145
+ download_and_extract_libs(matching_asset['browser_download_url'], platform_id)
146
+ print("\nArtifacts have been downloaded and extracted successfully!")
147
+ copy_artifacts_to_root()
148
+ else:
149
+ print(f"\nNo matching asset found: {expected_asset_name}")
150
+
151
+ except requests.exceptions.RequestException as e:
152
+ print(f"Error: {e}")
153
+ sys.exit(1)
154
+ except Exception as e:
155
+ print(f"Error: {e}")
156
+ sys.exit(1)
157
+
158
+ if __name__ == "__main__":
159
+ main()
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+