IPTCInfo3 2.2.0__tar.gz → 2.3.1__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.
@@ -19,3 +19,6 @@ __pycache__
19
19
 
20
20
  # pipfile stuff
21
21
  Pipfile.lock
22
+
23
+ # virtualenvs
24
+ .venv/
@@ -1,3 +1,33 @@
1
+ ## 2.3.1
2
+
3
+ ### Compatibility
4
+ - Verified on Python 3.15 (all 8 tests pass); supports Python 3.10 through 3.15
5
+ - Dropped EOL interpreters: 3.8 (EOL Oct 2024) and 3.9 (EOL Oct 2025); `requires-python` is now `>=3.10`
6
+ - Added PyPI classifiers for Python 3.14 and 3.15
7
+
8
+ ### Bug Fixes
9
+ - Fixed `SyntaxWarning: 'return' in a 'finally' block` in `file_is_jpeg()` (return moved out of `finally`; behavior unchanged, file position still reset)
10
+
11
+ ### Build / Dev
12
+ - Fixed `pytest` config: `testpaths` pointed at a non-existent `tests/` dir so bare `pytest` collected nothing; now points at repo root so `iptcinfo_test.py` is found
13
+ - Bumped Ruff `target-version` from `py38` to `py310`
14
+ - Updated dev pins: `.python-version` 3.9.7 → 3.15, `Pipfile` requires 3.10, CircleCI image `cimg/python:3.9.7` → `cimg/python:3.15`
15
+ - Added GitHub Actions CI matrix (Python 3.10–3.15)
16
+
17
+ ---
18
+
19
+ ## 2.3.0 (2025-12-02)
20
+
21
+ ### Bug Fixes
22
+ - **Issue #50**: Fixed keywords and other list fields to prevent duplicate values (deduplicates on append and save)
23
+ - **Issue #45**: Removed outdated VERSION = 1.9 comment from code
24
+
25
+ ### New Features
26
+ - **Issue #44**: Added LICENSE file with dual Artistic-1.0 / GPL-1.0+ license
27
+ - Added `UniqueList` class for list fields that automatically prevents duplicates
28
+
29
+ ---
30
+
1
31
  ## 2.2.0 (2025-10-02)
2
32
 
3
33
  ### Bug Fixes
@@ -15,6 +45,13 @@
15
45
  - **Issue #38**: Verified backup file behavior (use `options={'overwrite': True}` to avoid ~ files)
16
46
  - Better error handling and logging throughout
17
47
 
48
+ ### Build System Modernization
49
+ - Migrated from legacy `setup.py` to modern `pyproject.toml`
50
+ - Now uses `uv` and `hatchling` for building (PEP 517/518 compliant)
51
+ - Simplified setup.py to minimal backward compatibility shim
52
+ - Added comprehensive publishing guide (`PUBLISHING.md`)
53
+ - Supports Python 3.8 through 3.13
54
+
18
55
  ### Notes
19
56
  - **Issue #39, #41**: Ready for PyPI release with all fixes from master branch
20
57
 
@@ -0,0 +1,20 @@
1
+ IPTCInfo3 - IPTC metadata extraction and modification for Python
2
+
3
+ Copyright (C) 2000-2004 Josh Carter <josh@multipart-mixed.com>
4
+ Copyright (C) 2004-2008 Tamas Gulacsi <gthomas@gthomas.hu>
5
+ Copyright (C) 2008-2025 James Campbell
6
+
7
+ DUAL LICENSE
8
+
9
+ This software is dual-licensed. You may choose either:
10
+
11
+ 1. The Artistic License 1.0
12
+ https://opensource.org/licenses/Artistic-1.0
13
+
14
+ 2. The GNU General Public License v1.0 or later
15
+ https://www.gnu.org/licenses/old-licenses/gpl-1.0.en.html
16
+
17
+ You may use, modify, and distribute this software under the terms of
18
+ either license, at your option.
19
+
20
+ Full license texts available at the URLs above.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: IPTCInfo3
3
- Version: 2.2.0
3
+ Version: 2.3.1
4
4
  Summary: Extract and modify IPTC metadata from JPEG images
5
5
  Project-URL: Homepage, https://github.com/jamesacampbell/iptcinfo3
6
6
  Project-URL: Repository, https://github.com/jamesacampbell/iptcinfo3
@@ -8,6 +8,7 @@ Project-URL: Issues, https://github.com/jamesacampbell/iptcinfo3/issues
8
8
  Author-email: Tamas Gulacsi <gthomas@fw.hu>
9
9
  Maintainer-email: James Campbell <jc@normail.co>
10
10
  License: Artistic-1.0 OR GPL-1.0-or-later
11
+ License-File: LICENSE
11
12
  Keywords: exif,iptc,jpeg,metadata,photography
12
13
  Classifier: Development Status :: 4 - Beta
13
14
  Classifier: Intended Audience :: Developers
@@ -15,15 +16,15 @@ Classifier: License :: OSI Approved :: Artistic License
15
16
  Classifier: License :: OSI Approved :: GNU General Public License (GPL)
16
17
  Classifier: Programming Language :: Python
17
18
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.8
19
- Classifier: Programming Language :: Python :: 3.9
20
19
  Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
22
21
  Classifier: Programming Language :: Python :: 3.12
23
22
  Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Programming Language :: Python :: 3.15
24
25
  Classifier: Topic :: Multimedia :: Graphics
25
26
  Classifier: Topic :: Utilities
26
- Requires-Python: >=3.8
27
+ Requires-Python: >=3.10
27
28
  Provides-Extra: dev
28
29
  Requires-Dist: pytest-cov>=4.0; extra == 'dev'
29
30
  Requires-Dist: pytest-watch>=4.2; extra == 'dev'
@@ -33,8 +34,6 @@ Description-Content-Type: text/x-rst
33
34
  ``IPTCINFO 3``
34
35
  ==============
35
36
 
36
- `Build Status <https://api.travis-ci.org/crccheck/iptcinfo3.png>`_
37
-
38
37
  Like IPTCInfo but finally compatible for Python 3
39
38
  -------------------------------------------------
40
39
 
@@ -43,7 +42,7 @@ Ported from Josh Carter's Perl IPTCInfo-1.9.pm by Tamas Gulacsi
43
42
 
44
43
  Ever wish you add information to your photos like a caption, the place
45
44
  you took it, the date, and perhaps even keywords and categories? You
46
- already can. The International Press Telecommunications Council (IPTC)
45
+ already can. The `International Press Telecommunications Council (IPTC) <https://en.wikipedia.org/wiki/International_Press_Telecommunications_Council>`_
47
46
  defines a format for exchanging meta-information in news content, and
48
47
  that includes photographs. You can embed all kinds of information in
49
48
  your images. The trick is putting it to use.
@@ -55,6 +54,27 @@ programs -- pull it back out. You can use the information directly in
55
54
  Python programs, export it to XML, or even export SQL statements ready
56
55
  to be fed into a database.
57
56
 
57
+ Wikidata item https://www.wikidata.org/wiki/Q137179516
58
+
59
+ Installation
60
+ ------------
61
+
62
+ Install from PyPI::
63
+
64
+ pip install IPTCInfo3
65
+
66
+ Or with uv::
67
+
68
+ uv pip install IPTCInfo3
69
+
70
+ On Gentoo Linux: on `gentoo-zh overlay <https://github.com/microcai/gentoo-zh/tree/master/dev-python/iptcinfo3>`_.
71
+
72
+ Requirements
73
+ ------------
74
+
75
+ - Python 3.10 or higher
76
+ - No external dependencies required
77
+
58
78
  Usage
59
79
  -----
60
80
 
@@ -85,3 +105,5 @@ Lists for keywords, so you can just append!
85
105
  Save new info to file
86
106
  ``info.save()``
87
107
  ``info.save_as('very_meta.jpg')``
108
+
109
+ For real life usage example see https://gitlab.com/vitaly-zdanevich/upload_to_commons_with_categories_from_iptc/-/blob/master/upload_to_commons_with_categories_from_iptc.py
@@ -1,8 +1,6 @@
1
1
  ``IPTCINFO 3``
2
2
  ==============
3
3
 
4
- `Build Status <https://api.travis-ci.org/crccheck/iptcinfo3.png>`_
5
-
6
4
  Like IPTCInfo but finally compatible for Python 3
7
5
  -------------------------------------------------
8
6
 
@@ -11,7 +9,7 @@ Ported from Josh Carter's Perl IPTCInfo-1.9.pm by Tamas Gulacsi
11
9
 
12
10
  Ever wish you add information to your photos like a caption, the place
13
11
  you took it, the date, and perhaps even keywords and categories? You
14
- already can. The International Press Telecommunications Council (IPTC)
12
+ already can. The `International Press Telecommunications Council (IPTC) <https://en.wikipedia.org/wiki/International_Press_Telecommunications_Council>`_
15
13
  defines a format for exchanging meta-information in news content, and
16
14
  that includes photographs. You can embed all kinds of information in
17
15
  your images. The trick is putting it to use.
@@ -23,6 +21,27 @@ programs -- pull it back out. You can use the information directly in
23
21
  Python programs, export it to XML, or even export SQL statements ready
24
22
  to be fed into a database.
25
23
 
24
+ Wikidata item https://www.wikidata.org/wiki/Q137179516
25
+
26
+ Installation
27
+ ------------
28
+
29
+ Install from PyPI::
30
+
31
+ pip install IPTCInfo3
32
+
33
+ Or with uv::
34
+
35
+ uv pip install IPTCInfo3
36
+
37
+ On Gentoo Linux: on `gentoo-zh overlay <https://github.com/microcai/gentoo-zh/tree/master/dev-python/iptcinfo3>`_.
38
+
39
+ Requirements
40
+ ------------
41
+
42
+ - Python 3.10 or higher
43
+ - No external dependencies required
44
+
26
45
  Usage
27
46
  -----
28
47
 
@@ -53,3 +72,5 @@ Lists for keywords, so you can just append!
53
72
  Save new info to file
54
73
  ``info.save()``
55
74
  ``info.save_as('very_meta.jpg')``
75
+
76
+ For real life usage example see https://gitlab.com/vitaly-zdanevich/upload_to_commons_with_categories_from_iptc/-/blob/master/upload_to_commons_with_categories_from_iptc.py
@@ -14,7 +14,6 @@
14
14
  # it under the terms of the Artistic License or the GNU General Public
15
15
  # License (GPL). You may choose either license.
16
16
  #
17
- # VERSION = '1.9';
18
17
  """
19
18
  IPTCInfo - Python module for extracting and modifying IPTC image meta-data
20
19
  """
@@ -27,7 +26,7 @@ import tempfile
27
26
  from struct import pack, unpack
28
27
  import json
29
28
 
30
- __version__ = '2.2.0'
29
+ __version__ = '2.3.1'
31
30
  __author__ = 'Gulácsi, Tamás'
32
31
  __updated_by__ = 'Campbell, James'
33
32
 
@@ -163,7 +162,7 @@ def file_is_jpeg(fh):
163
162
  ered = ff == 0xff
164
163
  finally:
165
164
  fh.seek(0)
166
- return ered
165
+ return ered
167
166
 
168
167
 
169
168
  def jpeg_get_variable_length(fh):
@@ -524,6 +523,17 @@ c_charset = {100: 'iso8859_1', 101: 'iso8859_2', 109: 'iso8859_3',
524
523
  c_charset_r = {v: k for k, v in c_charset.items()}
525
524
 
526
525
 
526
+ class UniqueList(list):
527
+ """A list that prevents duplicate values on append/extend."""
528
+ def append(self, value):
529
+ if value not in self:
530
+ super().append(value)
531
+
532
+ def extend(self, values):
533
+ for v in values:
534
+ self.append(v)
535
+
536
+
527
537
  class IPTCData(dict):
528
538
  """Dict with int/string keys from c_listdatanames"""
529
539
  def __init__(self, diction={}, *args, **kwds):
@@ -608,9 +618,9 @@ class IPTCInfo:
608
618
 
609
619
  def __init__(self, fobj, force=False, inp_charset=None, out_charset=None):
610
620
  self._data = IPTCData({
611
- 'supplemental category': [],
612
- 'keywords': [],
613
- 'contact': [],
621
+ 'supplemental category': UniqueList(),
622
+ 'keywords': UniqueList(),
623
+ 'contact': UniqueList(),
614
624
  })
615
625
  self._fobj = fobj
616
626
  self._force = force
@@ -956,10 +966,11 @@ class IPTCInfo:
956
966
  out.append(pack("!BBBH", tag, record, dataset, len(value)))
957
967
  out.append(value)
958
968
  else:
969
+ seen = set()
959
970
  for v in map(bytes, value):
960
- if v is None or len(v) == 0:
971
+ if v is None or len(v) == 0 or v in seen:
961
972
  continue
962
-
973
+ seen.add(v)
963
974
  out.append(pack("!BBBH", tag, record, dataset, len(v)))
964
975
  out.append(v)
965
976
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "IPTCInfo3"
7
- version = "2.2.0"
7
+ version = "2.3.1"
8
8
  description = "Extract and modify IPTC metadata from JPEG images"
9
9
  readme = "README.rst"
10
10
  license = { text = "Artistic-1.0 OR GPL-1.0-or-later" }
@@ -22,16 +22,16 @@ classifiers = [
22
22
  "Intended Audience :: Developers",
23
23
  "Programming Language :: Python",
24
24
  "Programming Language :: Python :: 3",
25
- "Programming Language :: Python :: 3.8",
26
- "Programming Language :: Python :: 3.9",
27
25
  "Programming Language :: Python :: 3.10",
28
26
  "Programming Language :: Python :: 3.11",
29
27
  "Programming Language :: Python :: 3.12",
30
28
  "Programming Language :: Python :: 3.13",
29
+ "Programming Language :: Python :: 3.14",
30
+ "Programming Language :: Python :: 3.15",
31
31
  "Topic :: Multimedia :: Graphics",
32
32
  "Topic :: Utilities",
33
33
  ]
34
- requires-python = ">=3.8"
34
+ requires-python = ">=3.10"
35
35
  dependencies = []
36
36
 
37
37
  [project.urls]
@@ -59,7 +59,7 @@ packages = ["."]
59
59
  only-include = ["iptcinfo3.py"]
60
60
 
61
61
  [tool.pytest.ini_options]
62
- testpaths = ["tests"]
62
+ testpaths = ["."]
63
63
  python_files = ["test_*.py", "*_test.py"]
64
64
 
65
65
  [tool.coverage.run]
@@ -72,7 +72,7 @@ skip_covered = true
72
72
 
73
73
  [tool.ruff]
74
74
  line-length = 100
75
- target-version = "py38"
75
+ target-version = "py310"
76
76
 
77
77
  [tool.ruff.lint]
78
78
  select = ["E", "F", "W", "I", "N", "UP"]