puremagic 2.2.0__tar.gz → 2.2.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.
- puremagic-2.2.1/.github/workflows/master-source-guard.yml +27 -0
- puremagic-2.2.1/.github/workflows/pythonpublish.yml +69 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/.github/workflows/tests.yml +7 -6
- {puremagic-2.2.0 → puremagic-2.2.1}/.pre-commit-config.yaml +4 -4
- {puremagic-2.2.0 → puremagic-2.2.1}/CHANGELOG.md +13 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/PKG-INFO +2 -1
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/__init__.py +1 -2
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/magic_data.json +16 -3
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/main.py +36 -40
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/cfbf_scanner.py +2 -3
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/mpeg_audio_scanner.py +41 -47
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/python_scanner.py +2 -3
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/sndhdr_scanner.py +2 -3
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/text_scanner.py +23 -10
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/zip_scanner.py +12 -1
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic.egg-info/PKG-INFO +2 -1
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic.egg-info/SOURCES.txt +5 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/pyproject.toml +20 -12
- puremagic-2.2.1/requirements-test.txt +3 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/scripts/parse_ftk_kessler_sigs.py +1 -1
- puremagic-2.2.1/test/resources/office/test_html.xls +62 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/test_common_extensions.py +47 -9
- puremagic-2.2.1/test/test_csv_quotes.py +72 -0
- puremagic-2.2.1/test/test_ftyp.py +39 -0
- puremagic-2.2.1/test/test_odf_templates.py +33 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/test_scanners.py +13 -4
- puremagic-2.2.1/uv.lock +981 -0
- puremagic-2.2.0/.github/workflows/pythonpublish.yml +0 -35
- puremagic-2.2.0/requirements-test.txt +0 -3
- puremagic-2.2.0/uv.lock +0 -882
- {puremagic-2.2.0 → puremagic-2.2.1}/.claude/skills/changelog.md +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/.coveragerc +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/.gitignore +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/AUTHORS.rst +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/LICENSE +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/MANIFEST.in +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/README.rst +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/__main__.py +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/py.typed +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/__init__.py +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/asf_scanner.py +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/ebml_scanner.py +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/hdf5_scanner.py +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/helpers.py +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/json_scanner.py +1 -1
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/ogg_scanner.py +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic/scanners/pdf_scanner.py +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic.egg-info/dependency_links.txt +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic.egg-info/entry_points.txt +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/puremagic.egg-info/top_level.txt +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/setup.cfg +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/__init__.py +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/common.py +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.7z +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.big_endian.pcapng +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.bz2 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.cb7 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.cbt +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.cbz +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.fb2.zip +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.fbz +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.gz +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.little_endian.pcapng +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.pcapng +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.rar +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.tar +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.txt.lz4 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.txt.zst +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.xz +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/archive/test.zip +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test.aac +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test.aif +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test.flac +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test.oga +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test.ogg +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test.opus +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test.sndr +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test.wav +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test.wma +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test_mp3_vbr_info_128k_notags.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test_mp3_vbr_xing_128k_3di_id3v1.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test_mp3_vbr_xing_128k_apev1.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test_mp3_vbr_xing_128k_apev1_id3v1.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test_mp3_vbr_xing_128k_apev2_tagplus_id3v1.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test_mp3_vbr_xing_128k_ext_id3v1.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test_mp3_vbr_xing_128k_lyrics3v2_id3v1.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test_mp3_vbr_xing_128k_notags.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test_mp3_vbr_xing_128k_tagplus_apev2_id3v1.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test_mp3_vbr_xing_128k_tagplus_id3v1.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/audio/test_mpeg2_mp3_VBR_128k_id3v2_24.mp3 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/fake_file +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.avif +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.bmp +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.gif +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.ico +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.jpg +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.png +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.psd +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.svg +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.tga +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.tif +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.webp +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test.xcf +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test_varriant.svg +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/images/test_varriant_2.svg +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/media/test (single).vmdk +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/media/test (split).vmdk +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/media/test.iso +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/media/test.mdf +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/media/test.swf +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.doc +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.docm +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.docx +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.dotm +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.dotx +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.eml +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.fb2 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.msg +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.odp +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.ods +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.odt +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.pdf +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.potm +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.potx +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.ppt +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.pptm +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.pptx +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.txt +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.xls +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.xlsb +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.xlsm +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.xlsx +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.xltm +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test.xltx +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/test_utf16le.csv +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/text_cr.txt +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/text_crlf.txt +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/office/text_lf.txt +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/system/test.exe +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/system/test.json +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/system/test.puremagic_multi_footer +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/system/test.py +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/system/test_list.json +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/video/test.3g2 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/video/test.avi +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/video/test.flv +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/video/test.jxsv +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/video/test.mkv +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/video/test.mp4 +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/video/test.mpg +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/video/test.ogv +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/video/test.webm +0 -0
- {puremagic-2.2.0 → puremagic-2.2.1}/test/resources/video/test.wmv +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Releases only: every change lands on develop first, then ships to master
|
|
2
|
+
# as a single versioned release PR from develop.
|
|
3
|
+
|
|
4
|
+
name: Master Source Guard
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [ master ]
|
|
9
|
+
types: [ opened, reopened, synchronize, edited ]
|
|
10
|
+
|
|
11
|
+
permissions: {}
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
require-develop:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
timeout-minutes: 5
|
|
17
|
+
steps:
|
|
18
|
+
- name: Require PRs into master to come from develop
|
|
19
|
+
env:
|
|
20
|
+
HEAD_REF: ${{ github.head_ref }}
|
|
21
|
+
HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
|
|
22
|
+
BASE_REPO: ${{ github.repository }}
|
|
23
|
+
run: |
|
|
24
|
+
if [ "$HEAD_REF" != "develop" ] || [ "$HEAD_REPO" != "$BASE_REPO" ]; then
|
|
25
|
+
echo "::error::PRs into master must come from $BASE_REPO:develop (got $HEAD_REPO:$HEAD_REF). Please retarget this PR to develop."
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Builds and publishes to PyPI when a GitHub release is created.
|
|
2
|
+
# Uses PyPI Trusted Publishing (OIDC), so no PyPI password or token is stored in GitHub:
|
|
3
|
+
# https://docs.pypi.org/trusted-publishers/
|
|
4
|
+
# All actions are pinned to full commit SHAs; the comment shows the matching release tag.
|
|
5
|
+
|
|
6
|
+
name: Upload Python Package
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
release:
|
|
10
|
+
types: [created]
|
|
11
|
+
|
|
12
|
+
permissions: {}
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
build:
|
|
16
|
+
name: Build distribution
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 15
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
24
|
+
with:
|
|
25
|
+
persist-credentials: false
|
|
26
|
+
|
|
27
|
+
- name: Set up Python
|
|
28
|
+
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
29
|
+
with:
|
|
30
|
+
python-version: '3.15'
|
|
31
|
+
|
|
32
|
+
- name: Install uv
|
|
33
|
+
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
34
|
+
|
|
35
|
+
- name: Install dependencies
|
|
36
|
+
run: uv sync --frozen
|
|
37
|
+
|
|
38
|
+
- name: Build and check distribution
|
|
39
|
+
run: |
|
|
40
|
+
uv build
|
|
41
|
+
uv run twine check dist/*
|
|
42
|
+
|
|
43
|
+
- name: Store distribution
|
|
44
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
45
|
+
with:
|
|
46
|
+
name: python-package-distributions
|
|
47
|
+
path: dist/
|
|
48
|
+
if-no-files-found: error
|
|
49
|
+
|
|
50
|
+
publish:
|
|
51
|
+
name: Publish to PyPI
|
|
52
|
+
needs: build
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
timeout-minutes: 15
|
|
55
|
+
environment:
|
|
56
|
+
name: pypi
|
|
57
|
+
url: https://pypi.org/p/puremagic
|
|
58
|
+
permissions:
|
|
59
|
+
id-token: write # Required for Trusted Publishing; the only permission this job has
|
|
60
|
+
|
|
61
|
+
steps:
|
|
62
|
+
- name: Download distribution
|
|
63
|
+
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
64
|
+
with:
|
|
65
|
+
name: python-package-distributions
|
|
66
|
+
path: dist/
|
|
67
|
+
|
|
68
|
+
- name: Publish to PyPI
|
|
69
|
+
uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
|
|
@@ -16,24 +16,25 @@ jobs:
|
|
|
16
16
|
fail-fast: false
|
|
17
17
|
matrix:
|
|
18
18
|
os: [ubuntu-latest]
|
|
19
|
-
python-version: ["3.12", "3.13", "3.14"]
|
|
19
|
+
python-version: ["3.12", "3.13", "3.14", "3.15"]
|
|
20
20
|
include:
|
|
21
21
|
- os: macos-latest
|
|
22
|
-
python-version: '3.
|
|
22
|
+
python-version: '3.15'
|
|
23
23
|
- os: windows-latest
|
|
24
|
-
python-version: '3.
|
|
24
|
+
python-version: '3.15'
|
|
25
25
|
runs-on: ${{ matrix.os }}
|
|
26
|
+
timeout-minutes: 15
|
|
26
27
|
steps:
|
|
27
|
-
- uses: actions/checkout@
|
|
28
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
28
29
|
|
|
29
30
|
- name: Set up Python ${{ matrix.python-version }}
|
|
30
|
-
uses: actions/setup-python@
|
|
31
|
+
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
31
32
|
with:
|
|
32
33
|
python-version: ${{ matrix.python-version }}
|
|
33
34
|
allow-prereleases: true
|
|
34
35
|
|
|
35
36
|
- name: Install the latest version of uv and activate the environment
|
|
36
|
-
uses: astral-sh/setup-uv@
|
|
37
|
+
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
37
38
|
|
|
38
39
|
- name: Install dependencies
|
|
39
40
|
run: uv sync --frozen
|
|
@@ -35,7 +35,7 @@ repos:
|
|
|
35
35
|
|
|
36
36
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
37
37
|
# Ruff version.
|
|
38
|
-
rev: v0.
|
|
38
|
+
rev: v0.17.3
|
|
39
39
|
hooks:
|
|
40
40
|
# Run the linter.
|
|
41
41
|
- id: ruff-check
|
|
@@ -53,7 +53,7 @@ repos:
|
|
|
53
53
|
pass_filenames: false
|
|
54
54
|
|
|
55
55
|
- repo: https://github.com/codespell-project/codespell
|
|
56
|
-
rev: v2.4.
|
|
56
|
+
rev: v2.4.3
|
|
57
57
|
hooks:
|
|
58
58
|
- id: codespell
|
|
59
59
|
args: [--ignore-words-list, "addin,caf,tye,wors"]
|
|
@@ -64,11 +64,11 @@ repos:
|
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
|
67
|
-
rev: v2.
|
|
67
|
+
rev: v2.30.3
|
|
68
68
|
hooks:
|
|
69
69
|
- id: pyproject-fmt
|
|
70
70
|
|
|
71
71
|
- repo: https://github.com/abravalheri/validate-pyproject
|
|
72
|
-
rev: v0.
|
|
72
|
+
rev: v0.27
|
|
73
73
|
hooks:
|
|
74
74
|
- id: validate-pyproject
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
Version 2.2.1
|
|
5
|
+
-------------
|
|
6
|
+
|
|
7
|
+
- Fixing #150 shell scripts with `#!/bin/sh`, `bash`, or `zsh` shebangs are now identified as `.sh` / `application/x-shellscript` instead of plain text (thanks to jbleyel and Sai Asish Y)
|
|
8
|
+
- Fixing #152 CSV detection only sniffs a bounded sample, so large files (such as HTML exported from Excel as `.xls`) no longer take minutes to identify, and HTML starting with an uppercase `<HTML` tag is detected (thanks to nikilase)
|
|
9
|
+
- Fixing #156 ISO media files (HEIC, AVIF, MP4, 3GP, MOV) are identified by their `ftyp` major brand instead of the box size (thanks to Nick Gigliotti and zhouh)
|
|
10
|
+
- Fixing #157 CSV files with quoted delimiters, escaped quotes, or multiline fields are no longer rejected (thanks to zhouh)
|
|
11
|
+
- Fixing #164 JPEG data without a filename hint now reports `.jpg` instead of `.jfif` (thanks to Gil Forcada Codinachs)
|
|
12
|
+
- Fixing OpenDocument templates (`.ott`, `.ots`, `.otp`) are no longer reported as regular documents during ZIP scanning (thanks to pabszero)
|
|
13
|
+
- Fixing file extensions are taken from the file's base name, so dots in directory names no longer produce bogus extensions (thanks to rui-zju)
|
|
14
|
+
- Adding Python 3.15 to tested and supported versions
|
|
15
|
+
- Changing PyPI releases to use Trusted Publishing, with GitHub Actions pinned to commit SHAs and upgraded off the deprecated Node.js 20 runtime
|
|
16
|
+
|
|
4
17
|
Version 2.2.0
|
|
5
18
|
-------------
|
|
6
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: puremagic
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: Pure python implementation of magic file detection
|
|
5
5
|
Author-email: Chris Griffith <chris@cdgriffith.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
17
18
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
18
19
|
Classifier: Topic :: Utilities
|
|
19
20
|
Requires-Python: >=3.12
|
|
@@ -733,6 +733,7 @@
|
|
|
733
733
|
["437265617469766520566f6963652046696c651a", 0, ".voc", "audio/x-voc", "Creative Voice File"],
|
|
734
734
|
["595556344d504547",0, ".y4m", "video/x-yuv4mpeg", "YUV4MPEG2 video file"],
|
|
735
735
|
["3c68746d6c", 0, ".html", "text/html", "HTML File"],
|
|
736
|
+
["3c48544d4c", 0, ".html", "text/html", "HTML File"],
|
|
736
737
|
["424c5545", 0, ".bvr", "", "Blue Iris Video File"],
|
|
737
738
|
["2d2d2d2d2d424547494e20504b4353372d2d2d2d2d", 0, ".p7b", "", "PKCS 7 Certificate File" ],
|
|
738
739
|
["7b22", 0, ".json", "application/json", "JSON File"],
|
|
@@ -856,6 +857,7 @@
|
|
|
856
857
|
],
|
|
857
858
|
["7a626578", 0, ".info", "", "ZoomBrowser Image Index"],
|
|
858
859
|
["425a68", 0, ".tbz2", "application/x-bzip2", "bzip2 compressed archive"],
|
|
860
|
+
["ffd8ff", 0, ".jpg", "image/jpeg", "JPEG|EXIF|SPIFF images"],
|
|
859
861
|
["ffd8ff", 0, ".jfif", "image/jpeg", "JPEG|EXIF|SPIFF images"],
|
|
860
862
|
["514649", 0, ".qemu", "", "Qcow Disk Image"],
|
|
861
863
|
["504b5c3030335c303034", 0, ".epub", "application/epub+zip", "electronic book document"],
|
|
@@ -903,6 +905,19 @@
|
|
|
903
905
|
["23212f7573722f6c6f63616c2f62696e2f707974686f6e", 0, ".wsgi", "text/x-python", "Python script"],
|
|
904
906
|
["2321202f7573722f6c6f63616c2f62696e2f707974686f6e", 0, ".wsgi", "text/x-python", "Python script"],
|
|
905
907
|
["6576616c205c2265786563202f7573722f6c6f63616c2f62696e2f707974686f6e", 0, ".wsgi", "text/x-python", "Python script"],
|
|
908
|
+
["23212f62696e2f7368", 0, ".sh", "application/x-sh", "POSIX shell script"],
|
|
909
|
+
["2321202f62696e2f7368", 0, ".sh", "application/x-sh", "POSIX shell script"],
|
|
910
|
+
["23212f7573722f62696e2f7368", 0, ".sh", "application/x-sh", "POSIX shell script"],
|
|
911
|
+
["23212f7573722f62696e2f656e762073680a", 0, ".sh", "application/x-sh", "POSIX shell script"],
|
|
912
|
+
["23212f62696e2f64617368", 0, ".sh", "application/x-sh", "Dash shell script"],
|
|
913
|
+
["23212f62696e2f62617368", 0, ".sh", "application/x-shellscript", "Bash shell script"],
|
|
914
|
+
["2321202f62696e2f62617368", 0, ".sh", "application/x-shellscript", "Bash shell script"],
|
|
915
|
+
["23212f7573722f62696e2f62617368", 0, ".sh", "application/x-shellscript", "Bash shell script"],
|
|
916
|
+
["23212f7573722f62696e2f656e762062617368", 0, ".sh", "application/x-shellscript", "Bash shell script"],
|
|
917
|
+
["23212f7573722f6c6f63616c2f62696e2f62617368", 0, ".sh", "application/x-shellscript", "Bash shell script"],
|
|
918
|
+
["23212f62696e2f7a7368", 0, ".sh", "application/x-shellscript", "Zsh shell script"],
|
|
919
|
+
["23212f7573722f62696e2f7a7368", 0, ".sh", "application/x-shellscript", "Zsh shell script"],
|
|
920
|
+
["23212f7573722f62696e2f656e76207a73680a", 0, ".sh", "application/x-shellscript", "Zsh shell script"],
|
|
906
921
|
["53756d6d6172793a20", 0, ".spec", "text/x-rpm-spec", "RPM spec file"],
|
|
907
922
|
["25646566696e6520", 0, ".spec", "text/x-rpm-spec", "RPM spec file"],
|
|
908
923
|
["667479707174", 4, ".qtvr", "video/quicktime", "QuickTime video"],
|
|
@@ -1144,7 +1159,6 @@
|
|
|
1144
1159
|
["b5a2b0b3b3b0a5b5", 0, ".cal", "", "Windows calendar"],
|
|
1145
1160
|
["537570657243616c", 0, ".cal", "", "SuperCalc worksheet"],
|
|
1146
1161
|
["504e4349554e444f", 0, ".dat", "", "Norton Disk Doctor undo file"],
|
|
1147
|
-
["0000001466747970", 0, ".3gp", "", "3GPP multimedia files"],
|
|
1148
1162
|
["5b57696e646f7773", 0, ".cpx", "", "Microsoft Code Page Translation file"],
|
|
1149
1163
|
["a90d000000000000", 0, ".dat", "", "Access Data FTK evidence"],
|
|
1150
1164
|
["424f4f4b4d4f4249", 0, ".prc", "", "Palmpilot resource file"],
|
|
@@ -1380,7 +1394,6 @@
|
|
|
1380
1394
|
["425a68", 0, ".bz2", "application/x-bzip2", "bzip2 compressed archive"],
|
|
1381
1395
|
["425a68", 0, ".tb2", "application/x-bzip2", "bzip2 compressed archive"],
|
|
1382
1396
|
["ffd8ff", 0, ".jpe", "image/jpeg", "JPEG|EXIF|SPIFF images"],
|
|
1383
|
-
["ffd8ff", 0, ".jpg", "image/jpeg", "JPEG|EXIF|SPIFF images"],
|
|
1384
1397
|
["2d6c68", 2, ".lha", "application/x-lha", "Compressed archive"],
|
|
1385
1398
|
["445644", 0, ".dvr", "", "DVR-Studio stream file"],
|
|
1386
1399
|
["1a0000", 0, ".ntf", "", "Lotus Notes database template"],
|
|
@@ -1655,6 +1668,7 @@
|
|
|
1655
1668
|
["fff0", 0, ".aac", "audio/aac", "AAC audio"],
|
|
1656
1669
|
["667479704d3441", 4, ".f4a", "audio/mp4", "MPEG-4 audio"],
|
|
1657
1670
|
["6674797069736f6d", 4, ".mp4", "video/mp4", "MPEG-4 video"],
|
|
1671
|
+
["667479706d703431", 4, ".mp4", "video/mp4", "MPEG-4 video"],
|
|
1658
1672
|
["667479706d703432", 4, ".mp4", "video/mp4", "MPEG-4 video"],
|
|
1659
1673
|
["667479704d534e56", 4, ".mp4", "video/mp4", "MPEG-4 video"],
|
|
1660
1674
|
["667479704d345620", 4, ".mp4", "video/mp4", "MPEG-4 video"],
|
|
@@ -2040,7 +2054,6 @@
|
|
|
2040
2054
|
["fff9", 0, ".aac", "audio/aac", "MPEG-2 AAC audio"],
|
|
2041
2055
|
["fff1", 0, ".aac", "audio/aac", "MPEG-4 AAC audio"],
|
|
2042
2056
|
["6674797033677035", 4, ".mp4", "video/mp4", "MPEG-4 video file"],
|
|
2043
|
-
["0000001c66747970", 0, ".mp4", "video/mp4", "MPEG-4 video"],
|
|
2044
2057
|
["fdffffff02", 512, ".pub", "", "MS Publisher file subheader"],
|
|
2045
2058
|
["53505642", 0, ".spvb", "", "MultiBit Bitcoin blockchain file"],
|
|
2046
2059
|
["0a166f72672e626974636f696e2e7072", 0, ".wallet", "", "MultiBit Bitcoin wallet file"],
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
1
|
"""
|
|
3
2
|
puremagic is a pure python module that will identify a file based off it's
|
|
4
3
|
magic numbers. It is designed to be minimalistic and inherently cross-platform
|
|
@@ -23,38 +22,39 @@ import puremagic
|
|
|
23
22
|
|
|
24
23
|
if os.getenv("PUREMAGIC_DEEPSCAN") != "0":
|
|
25
24
|
from puremagic.scanners import (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
asf_scanner,
|
|
26
|
+
cfbf_scanner,
|
|
27
|
+
ebml_scanner,
|
|
28
|
+
hdf5_scanner,
|
|
29
29
|
json_scanner,
|
|
30
|
-
python_scanner,
|
|
31
|
-
sndhdr_scanner,
|
|
32
30
|
mpeg_audio_scanner,
|
|
33
|
-
hdf5_scanner,
|
|
34
|
-
cfbf_scanner,
|
|
35
31
|
ogg_scanner,
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
pdf_scanner,
|
|
33
|
+
python_scanner,
|
|
34
|
+
sndhdr_scanner,
|
|
35
|
+
text_scanner,
|
|
36
|
+
zip_scanner,
|
|
38
37
|
)
|
|
39
38
|
|
|
40
39
|
__author__ = "Chris Griffith"
|
|
41
|
-
__version__ = "2.2.
|
|
40
|
+
__version__ = "2.2.1"
|
|
42
41
|
__all__ = [
|
|
43
|
-
"magic_file",
|
|
44
|
-
"magic_string",
|
|
45
|
-
"magic_stream",
|
|
46
|
-
"magic_extension",
|
|
47
|
-
"from_file",
|
|
48
|
-
"from_string",
|
|
49
|
-
"from_stream",
|
|
50
|
-
"from_extension",
|
|
51
|
-
"ext_from_filename",
|
|
52
42
|
"PureError",
|
|
53
43
|
"PureMagic",
|
|
54
44
|
"PureMagicWithConfidence",
|
|
45
|
+
"ext_from_filename",
|
|
46
|
+
"from_extension",
|
|
47
|
+
"from_file",
|
|
48
|
+
"from_stream",
|
|
49
|
+
"from_string",
|
|
50
|
+
"magic_extension",
|
|
51
|
+
"magic_file",
|
|
52
|
+
"magic_stream",
|
|
53
|
+
"magic_string",
|
|
55
54
|
]
|
|
56
55
|
|
|
57
56
|
here = os.path.abspath(os.path.dirname(__file__))
|
|
57
|
+
DEFAULT_MAGIC_DATA = os.path.join(here, "magic_data.json")
|
|
58
58
|
|
|
59
59
|
PureMagic = namedtuple(
|
|
60
60
|
"PureMagic",
|
|
@@ -88,7 +88,7 @@ class PureValueError(ValueError):
|
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
def magic_data(
|
|
91
|
-
filename: os.PathLike | str =
|
|
91
|
+
filename: os.PathLike | str = DEFAULT_MAGIC_DATA,
|
|
92
92
|
) -> tuple[list[PureMagic], list[PureMagic], list[PureMagic], dict[bytes, list[PureMagic]]]:
|
|
93
93
|
"""Read the magic file"""
|
|
94
94
|
with open(filename, encoding="utf-8") as f:
|
|
@@ -270,7 +270,7 @@ def ext_from_filename(filename: os.PathLike | str) -> str:
|
|
|
270
270
|
:return: the extension off the end (empty string if it can't find one)
|
|
271
271
|
"""
|
|
272
272
|
try:
|
|
273
|
-
base, ext =
|
|
273
|
+
base, ext = os.path.basename(filename).lower().rsplit(".", 1)
|
|
274
274
|
except ValueError:
|
|
275
275
|
return ""
|
|
276
276
|
ext = f".{ext}"
|
|
@@ -529,22 +529,18 @@ def run_deep_scan(
|
|
|
529
529
|
name=result.name,
|
|
530
530
|
)
|
|
531
531
|
]
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
mime_type=result.mime_type,
|
|
545
|
-
name=result.name,
|
|
546
|
-
)
|
|
547
|
-
]
|
|
532
|
+
result = catch_all_deep_scan(filename, head, foot)
|
|
533
|
+
if result:
|
|
534
|
+
return [
|
|
535
|
+
PureMagicWithConfidence(
|
|
536
|
+
confidence=result.confidence,
|
|
537
|
+
byte_match=None,
|
|
538
|
+
offset=None,
|
|
539
|
+
extension=result.extension,
|
|
540
|
+
mime_type=result.mime_type,
|
|
541
|
+
name=result.name,
|
|
542
|
+
)
|
|
543
|
+
]
|
|
548
544
|
if raise_on_none:
|
|
549
545
|
raise PureError("Could not identify file")
|
|
550
546
|
|
|
@@ -594,8 +590,8 @@ def run_deep_scan(
|
|
|
594
590
|
|
|
595
591
|
|
|
596
592
|
def command_line_entry(*args):
|
|
597
|
-
import sys
|
|
598
|
-
from argparse import ArgumentParser
|
|
593
|
+
import sys
|
|
594
|
+
from argparse import ArgumentParser
|
|
599
595
|
|
|
600
596
|
parser = ArgumentParser(
|
|
601
597
|
description=(
|
|
@@ -115,9 +115,8 @@ def main(file_path: os.PathLike, head: bytes, foot: bytes) -> Match | None:
|
|
|
115
115
|
return None
|
|
116
116
|
|
|
117
117
|
# Verify magic bytes
|
|
118
|
-
if head[:8] != match_bytes:
|
|
119
|
-
|
|
120
|
-
return None
|
|
118
|
+
if head[:8] != match_bytes and head[:4] != match_bytes_short:
|
|
119
|
+
return None
|
|
121
120
|
|
|
122
121
|
# Parse CFBF header
|
|
123
122
|
sector_shift = struct.unpack_from("<H", head, 30)[0]
|