numbers-parser 4.14.4__tar.gz → 4.16.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.
Files changed (121) hide show
  1. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/PKG-INFO +26 -28
  2. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/README.md +7 -3
  3. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/pyproject.toml +87 -76
  4. numbers_parser-4.16.1/setup.cfg +4 -0
  5. numbers_parser-4.16.1/src/build/extract_functions.py +104 -0
  6. numbers_parser-4.16.1/src/build/extract_mapping.py +102 -0
  7. numbers_parser-4.16.1/src/build/generate_fontmap.py +22 -0
  8. numbers_parser-4.16.1/src/build/generate_mapping.py +70 -0
  9. numbers_parser-4.16.1/src/build/protodump.py +353 -0
  10. numbers_parser-4.16.1/src/build/rename_proto_files.py +26 -0
  11. numbers_parser-4.16.1/src/build/replace_paths.py +15 -0
  12. numbers_parser-4.16.1/src/debug/dump_formats.py +29 -0
  13. numbers_parser-4.16.1/src/debug/dump_formula_nodes.py +117 -0
  14. numbers_parser-4.16.1/src/debug/dump_formulas.py +50 -0
  15. numbers_parser-4.16.1/src/debug/dump_metadata.py +39 -0
  16. numbers_parser-4.16.1/src/debug/dump_strokes.py +75 -0
  17. numbers_parser-4.16.1/src/debug/dump_tree.py +113 -0
  18. numbers_parser-4.16.1/src/debug/dump_uids.py +31 -0
  19. numbers_parser-4.16.1/src/debug/lldbutil.py +1451 -0
  20. numbers_parser-4.16.1/src/debug/profile_large_read.py +23 -0
  21. numbers_parser-4.16.1/src/debug/profile_large_write.py +20 -0
  22. numbers_parser-4.16.1/src/debug/sdiff-no-ids.py +69 -0
  23. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/__init__.py +1 -0
  24. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/_unpack_numbers.py +1 -7
  25. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/cell.py +28 -202
  26. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/constants.py +19 -6
  27. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/document.py +54 -11
  28. numbers_parser-4.16.1/src/numbers_parser/formula.py +689 -0
  29. numbers_parser-4.16.1/src/numbers_parser/generated/TSCEArchives_pb2.py +363 -0
  30. numbers_parser-4.16.1/src/numbers_parser/generated/TSSArchives_pb2.py +64 -0
  31. numbers_parser-4.16.1/src/numbers_parser/generated/TSTArchives_pb2.py +437 -0
  32. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/mapping.py +1 -2
  33. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/model.py +399 -188
  34. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/numbers_cache.py +1 -1
  35. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/numbers_uuid.py +6 -0
  36. numbers_parser-4.16.1/src/numbers_parser/tokenizer.py +548 -0
  37. numbers_parser-4.16.1/src/numbers_parser/xrefs.py +850 -0
  38. numbers_parser-4.16.1/src/numbers_parser.egg-info/PKG-INFO +490 -0
  39. numbers_parser-4.16.1/src/numbers_parser.egg-info/SOURCES.txt +114 -0
  40. numbers_parser-4.16.1/src/numbers_parser.egg-info/dependency_links.txt +1 -0
  41. numbers_parser-4.16.1/src/numbers_parser.egg-info/entry_points.txt +4 -0
  42. numbers_parser-4.16.1/src/numbers_parser.egg-info/requires.txt +8 -0
  43. numbers_parser-4.16.1/src/numbers_parser.egg-info/top_level.txt +1 -0
  44. numbers_parser-4.16.1/tests/test_all_formulas.py +88 -0
  45. numbers_parser-4.16.1/tests/test_api_change.py +28 -0
  46. numbers_parser-4.16.1/tests/test_borders.py +331 -0
  47. numbers_parser-4.16.1/tests/test_bullets.py +51 -0
  48. numbers_parser-4.16.1/tests/test_cat_numbers.py +283 -0
  49. numbers_parser-4.16.1/tests/test_categories.py +687 -0
  50. numbers_parser-4.16.1/tests/test_coverage.py +307 -0
  51. numbers_parser-4.16.1/tests/test_create_cells.py +82 -0
  52. numbers_parser-4.16.1/tests/test_csv2numbers.py +337 -0
  53. numbers_parser-4.16.1/tests/test_currency.py +25 -0
  54. numbers_parser-4.16.1/tests/test_folder.py +16 -0
  55. numbers_parser-4.16.1/tests/test_formatting.py +955 -0
  56. numbers_parser-4.16.1/tests/test_formulas.py +404 -0
  57. numbers_parser-4.16.1/tests/test_issues.py +608 -0
  58. numbers_parser-4.16.1/tests/test_large.py +39 -0
  59. numbers_parser-4.16.1/tests/test_memory_leaks.py +28 -0
  60. numbers_parser-4.16.1/tests/test_merges.py +79 -0
  61. numbers_parser-4.16.1/tests/test_package.py +58 -0
  62. numbers_parser-4.16.1/tests/test_properties.py +56 -0
  63. numbers_parser-4.16.1/tests/test_roman.py +24 -0
  64. numbers_parser-4.16.1/tests/test_save.py +348 -0
  65. numbers_parser-4.16.1/tests/test_slices.py +144 -0
  66. numbers_parser-4.16.1/tests/test_styles.py +444 -0
  67. numbers_parser-4.16.1/tests/test_table_size.py +115 -0
  68. numbers_parser-4.16.1/tests/test_tables.py +203 -0
  69. numbers_parser-4.16.1/tests/test_unpack_numbers.py +206 -0
  70. numbers_parser-4.16.1/tests/test_unsupported.py +8 -0
  71. numbers_parser-4.16.1/tests/test_uuids.py +37 -0
  72. numbers_parser-4.16.1/tests/test_version.py +52 -0
  73. numbers_parser-4.14.4/src/numbers_parser/data/empty.numbers +0 -0
  74. numbers_parser-4.14.4/src/numbers_parser/formula.py +0 -322
  75. numbers_parser-4.14.4/src/numbers_parser/generated/TSCEArchives_pb2.py +0 -347
  76. numbers_parser-4.14.4/src/numbers_parser/generated/TSSArchives_pb2.py +0 -64
  77. numbers_parser-4.14.4/src/numbers_parser/generated/TSTArchives_pb2.py +0 -441
  78. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/LICENSE.rst +0 -0
  79. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/_cat_numbers.py +0 -0
  80. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/_csv2numbers.py +0 -0
  81. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/bullets.py +0 -0
  82. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/containers.py +0 -0
  83. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/currencies.py +0 -0
  84. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/exceptions.py +0 -0
  85. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/experimental.py +0 -0
  86. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TNArchives_pb2.py +0 -0
  87. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TNArchives_sos_pb2.py +0 -0
  88. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TNCommandArchives_pb2.py +0 -0
  89. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TNCommandArchives_sos_pb2.py +0 -0
  90. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSAArchives_pb2.py +0 -0
  91. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSAArchives_sos_pb2.py +0 -0
  92. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSACommandArchives_sos_pb2.py +0 -0
  93. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSCH3DArchives_pb2.py +0 -0
  94. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSCHArchives_Common_pb2.py +0 -0
  95. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSCHArchives_GEN_pb2.py +0 -0
  96. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSCHArchives_pb2.py +0 -0
  97. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSCHArchives_sos_pb2.py +0 -0
  98. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSCHCommandArchives_pb2.py +0 -0
  99. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSCHPreUFFArchives_pb2.py +0 -0
  100. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSCKArchives_pb2.py +0 -0
  101. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSCKArchives_sos_pb2.py +0 -0
  102. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSDArchives_pb2.py +0 -0
  103. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSDArchives_sos_pb2.py +0 -0
  104. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSDCommandArchives_pb2.py +0 -0
  105. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSKArchives_pb2.py +0 -0
  106. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSPArchiveMessages_pb2.py +0 -0
  107. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSPDatabaseMessages_pb2.py +0 -0
  108. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSPMessages_pb2.py +0 -0
  109. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSSArchives_sos_pb2.py +0 -0
  110. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSTArchives_sos_pb2.py +0 -0
  111. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSTCommandArchives_pb2.py +0 -0
  112. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSTStylePropertyArchiving_pb2.py +0 -0
  113. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSWPArchives_pb2.py +0 -0
  114. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSWPArchives_sos_pb2.py +0 -0
  115. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/TSWPCommandArchives_pb2.py +0 -0
  116. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/__init__.py +0 -0
  117. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/fontmap.py +0 -0
  118. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/generated/functionmap.py +0 -0
  119. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/iwafile.py +0 -0
  120. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/iwork.py +0 -0
  121. {numbers_parser-4.14.4 → numbers_parser-4.16.1}/src/numbers_parser/roman.py +0 -0
@@ -1,31 +1,26 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: numbers-parser
3
- Version: 4.14.4
3
+ Version: 4.16.1
4
4
  Summary: Read and write Apple Numbers spreadsheets
5
- License: MIT
6
- Author: Jon Connell
7
- Author-email: python@figsandfudge.com
8
- Requires-Python: >=3.9,<4.0
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.9
13
- Classifier: Programming Language :: Python :: 3.10
14
- Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Classifier: Programming Language :: Python :: 3.13
5
+ Author-email: Jon Connell <python@figsandfudge.com>
6
+ License-Expression: MIT
7
+ Project-URL: repository, https://github.com/masaccio/numbers-parser
8
+ Project-URL: documentation, https://github.com/masaccio/numbers-parser/blob/main/README.md
17
9
  Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
18
- Requires-Dist: compact-json (>=1.1.3,<2.0.0)
19
- Requires-Dist: enum-tools (>=0.11)
20
- Requires-Dist: importlib-resources (>=6.1)
21
- Requires-Dist: protobuf
22
- Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
23
- Requires-Dist: python-snappy (>=0.7,<0.8)
24
- Requires-Dist: setuptools (>=70.0.0)
25
- Requires-Dist: sigfig (>=1.3.3,<2.0.0)
26
- Project-URL: Documentation, https://github.com/masaccio/numbers-parser/blob/main/README.md
27
- Project-URL: Repository, https://github.com/masaccio/numbers-parser
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: <4.0,>=3.9
28
13
  Description-Content-Type: text/markdown
14
+ License-File: LICENSE.rst
15
+ Requires-Dist: compact-json<2.0.0,>=1.1.3
16
+ Requires-Dist: protobuf<6.0,>=4.0
17
+ Requires-Dist: python-snappy<1.0,>=0.7
18
+ Requires-Dist: sigfig<2.0.0,>=1.3.3
19
+ Requires-Dist: setuptools>=70.0.0
20
+ Requires-Dist: importlib-resources>=6.1
21
+ Requires-Dist: enum-tools>=0.11
22
+ Requires-Dist: python-dateutil<3.0.0.0,>=2.9.0.post0
23
+ Dynamic: license-file
29
24
 
30
25
  # numbers-parser
31
26
 
@@ -42,7 +37,9 @@ with earlier versions of Python.
42
37
 
43
38
  A pre-requisite for this package is [python-snappy](https://pypi.org/project/python-snappy/) which will be installed by Python automatically, but python-snappy also requires binary libraries for snappy compression.
44
39
 
45
- The most straightforward way to install the binary dependencies is to use [Homebrew](https://brew.sh) and source Python from Homebrew rather than from macOS as described in the [python-snappy github](https://github.com/andrix/python-snappy). Using [pipx](https://pipx.pypa.io/stable/installation/) for package management is also strongly recommended:
40
+ The most straightforward way to install the binary dependencies is to use
41
+ [Homebrew](https://brew.sh) and source Python from Homebrew rather than from macOS as described
42
+ in the [python-snappy github](https://github.com/andrix/python-snappy). Using [pipx](https://pipx.pypa.io/stable/installation/) for package management is also strongly recommended:
46
43
 
47
44
  ```bash
48
45
  brew install snappy python3 pipx
@@ -53,7 +50,6 @@ For Linux (your package manager may be different):
53
50
 
54
51
  ```bash
55
52
  sudo apt-get -y install libsnappy-dev
56
- python3 -m pip install numbers-parser
57
53
  ```
58
54
 
59
55
  On Windows, you will need to either arrange for snappy to be found for VSC++ or you can install python
@@ -62,7 +58,6 @@ for Windows on Arm. There appear to be no x86 pre-compiled packages for Windows.
62
58
 
63
59
  ```text
64
60
  pip install python_snappy-0.6.1-cp312-cp312-win_arm64.whl
65
- python3 -m pip install numbers-parser
66
61
  ```
67
62
 
68
63
  ## Quick Start
@@ -485,8 +480,11 @@ The following limitations are expected to always remain:
485
480
  - Password-encrypted documents cannot be opened. You must first re-save without
486
481
  a password to read (see [issue 88](https://github.com/masaccio/numbers-parser/issues/88) for details).
487
482
  A UnsupportedError exception is raised when such documents are opened.
483
+ - Due to changes in the format of Numbers documents, decoding of category groups
484
+ (introduced in `numbers-parser` version 4.16) is supported only for documents
485
+ created by Numbers 12.0 and later. No warnings are issued for earlier
486
+ Numbers documents.
488
487
 
489
488
  ## License
490
489
 
491
490
  All code in this repository is licensed under the [MIT License](https://github.com/masaccio/numbers-parser/blob/master/LICENSE.rst).
492
-
@@ -13,7 +13,9 @@ with earlier versions of Python.
13
13
 
14
14
  A pre-requisite for this package is [python-snappy](https://pypi.org/project/python-snappy/) which will be installed by Python automatically, but python-snappy also requires binary libraries for snappy compression.
15
15
 
16
- The most straightforward way to install the binary dependencies is to use [Homebrew](https://brew.sh) and source Python from Homebrew rather than from macOS as described in the [python-snappy github](https://github.com/andrix/python-snappy). Using [pipx](https://pipx.pypa.io/stable/installation/) for package management is also strongly recommended:
16
+ The most straightforward way to install the binary dependencies is to use
17
+ [Homebrew](https://brew.sh) and source Python from Homebrew rather than from macOS as described
18
+ in the [python-snappy github](https://github.com/andrix/python-snappy). Using [pipx](https://pipx.pypa.io/stable/installation/) for package management is also strongly recommended:
17
19
 
18
20
  ```bash
19
21
  brew install snappy python3 pipx
@@ -24,7 +26,6 @@ For Linux (your package manager may be different):
24
26
 
25
27
  ```bash
26
28
  sudo apt-get -y install libsnappy-dev
27
- python3 -m pip install numbers-parser
28
29
  ```
29
30
 
30
31
  On Windows, you will need to either arrange for snappy to be found for VSC++ or you can install python
@@ -33,7 +34,6 @@ for Windows on Arm. There appear to be no x86 pre-compiled packages for Windows.
33
34
 
34
35
  ```text
35
36
  pip install python_snappy-0.6.1-cp312-cp312-win_arm64.whl
36
- python3 -m pip install numbers-parser
37
37
  ```
38
38
 
39
39
  ## Quick Start
@@ -456,6 +456,10 @@ The following limitations are expected to always remain:
456
456
  - Password-encrypted documents cannot be opened. You must first re-save without
457
457
  a password to read (see [issue 88](https://github.com/masaccio/numbers-parser/issues/88) for details).
458
458
  A UnsupportedError exception is raised when such documents are opened.
459
+ - Due to changes in the format of Numbers documents, decoding of category groups
460
+ (introduced in `numbers-parser` version 4.16) is supported only for documents
461
+ created by Numbers 12.0 and later. No warnings are issued for earlier
462
+ Numbers documents.
459
463
 
460
464
  ## License
461
465
 
@@ -1,74 +1,85 @@
1
- [tool.poetry]
2
- authors = ["Jon Connell <python@figsandfudge.com>"]
1
+ [project]
2
+ authors = [
3
+ {name = "Jon Connell", email = "python@figsandfudge.com"},
4
+ ]
5
+ license = "MIT"
6
+ requires-python = "<4.0,>=3.9"
7
+ dependencies = [
8
+ "compact-json<2.0.0,>=1.1.3",
9
+ "protobuf<6.0,>=4.0",
10
+ "python-snappy<1.0,>=0.7",
11
+ "sigfig<2.0.0,>=1.3.3",
12
+ "setuptools>=70.0.0",
13
+ "importlib-resources>=6.1",
14
+ "enum-tools>=0.11",
15
+ "python-dateutil<3.0.0.0,>=2.9.0.post0",
16
+ ]
3
17
  classifiers = [
4
- "Topic :: Office/Business :: Financial :: Spreadsheet",
5
- "Programming Language :: Python :: 3",
6
- "Operating System :: OS Independent",
18
+ "Topic :: Office/Business :: Financial :: Spreadsheet",
19
+ "Programming Language :: Python :: 3",
20
+ "Operating System :: OS Independent",
7
21
  ]
8
22
  description = "Read and write Apple Numbers spreadsheets"
9
- documentation = "https://github.com/masaccio/numbers-parser/blob/main/README.md"
10
- license = "MIT"
11
23
  name = "numbers-parser"
12
- packages = [{include = "numbers_parser", from = "src"}]
13
24
  readme = "README.md"
25
+ version = "4.16.1"
26
+
27
+ [project.urls]
14
28
  repository = "https://github.com/masaccio/numbers-parser"
15
- version = "4.14.4"
29
+ documentation = "https://github.com/masaccio/numbers-parser/blob/main/README.md"
16
30
 
17
- [tool.poetry.scripts]
31
+ [project.scripts]
18
32
  cat-numbers = "numbers_parser._cat_numbers:main"
19
33
  unpack-numbers = "numbers_parser._unpack_numbers:main"
20
34
  csv2numbers = "numbers_parser._csv2numbers:main"
21
35
 
22
- [tool.poetry.dependencies]
23
- compact-json = "^1.1.3"
24
- protobuf = "*"
25
- python = ">=3.9,<4.0"
26
- python-snappy = "^0.7"
27
- sigfig = "^1.3.3"
28
- setuptools = ">=70.0.0"
29
- importlib-resources = ">=6.1"
30
- enum-tools = ">=0.11"
31
- python-dateutil = "^2.9.0.post0"
32
-
33
- [tool.poetry.group.dev.dependencies]
34
- gprof2dot = "^2022.7.29"
35
- line-profiler = "^4.0.3"
36
- mock = ">=5.1.0"
37
- pytest = ">=7.2.0"
38
- pytest-check = ">=1.0"
39
- pytest-console-scripts = "^1.3.1"
40
- pytest-cov = ">=4.0,>=5.0"
41
- pytest-xdist = "^3.3.1"
42
- ruff = "*"
43
- tox = "^4.11.4"
44
- python-magic = ">=0.4"
45
- tqdm = ">=4.66"
46
- colorama = "^0.4.6"
47
- roman = "^4.2"
48
- pympler = "^1.1"
49
-
50
- [tool.poetry.group.docs]
51
- optional = true
52
-
53
- [tool.poetry.group.docs.dependencies]
54
- sphinx = ">= 7.3"
55
- enum-tools = ">=0.11"
56
- sphinx-toolbox = ">=3.5"
57
- sphinx-nefertiti = ">=0.3.3"
58
- sphinx-markdown-builder = ">=0.6"
59
- sphinx-copybutton = ">=0.5"
60
-
61
- [tool.poetry.group.bootstrap]
62
- optional = true
63
-
64
- [tool.poetry.group.bootstrap.dependencies]
65
- pyobjc-core = ">=10.2"
66
- pyobjc-framework-Cocoa = ">=10.2"
67
- py2app = ">=0.28"
36
+ [dependency-groups]
37
+ dev = [
38
+ "gprof2dot<2023.0.0,>=2022.7.29",
39
+ "line-profiler<5.0.0,>=4.0.3",
40
+ "mock>=5.1.0",
41
+ "pytest>=7.2.0",
42
+ "pytest-check>=1.0",
43
+ "pytest-console-scripts<2.0.0,>=1.3.1",
44
+ "pytest-cov>=4.0,>=5.0",
45
+ "pytest-xdist<4.0.0,>=3.3.1",
46
+ "ruff",
47
+ "tox<5.0.0,>=4.11.4",
48
+ "python-magic>=0.4",
49
+ "tqdm>=4.66",
50
+ "colorama<1.0.0,>=0.4.6",
51
+ "pympler<2.0,>=1.1",
52
+ ]
53
+ docs = [
54
+ "sphinx>=7.3",
55
+ "enum-tools>=0.11",
56
+ "sphinx-toolbox>=3.5",
57
+ "sphinx-nefertiti>=0.7",
58
+ "sphinx-markdown-builder>=0.6",
59
+ "sphinx-copybutton>=0.5",
60
+ ]
61
+ bootstrap = [
62
+ "pyobjc-core>=10.2",
63
+ "pyobjc-framework-Cocoa>=10.2",
64
+ "py2app>=0.28",
65
+ ]
68
66
 
69
67
  [build-system]
70
- build-backend = "poetry.core.masonry.api"
71
- requires = ["poetry-core>=1.0.0"]
68
+ requires = ["setuptools >= 61.0"]
69
+ build-backend = "setuptools.build_meta"
70
+
71
+ # [tool.setuptools]
72
+ # exclude-package-data = { "*" = ["tests*"]}
73
+ # include-package-data = false
74
+ # packages.find.include = ['numbers-parser/src*']
75
+ # packages.find.exclude = ['tests*']
76
+ # package-dir = { "" = "src" }
77
+
78
+ [tool.setuptools.packages.find]
79
+ where = ["src"] # list of folders that contain the packages (["."] by default)
80
+ include = ["numbers_parser*"] # package names should match these glob patterns (["*"] by default)
81
+ exclude = ["*tests*"] # exclude packages matching these glob patterns (empty by default)
82
+ namespaces = false # to disable scanning PEP 420 namespaces (true by default)
72
83
 
73
84
  [tool.coverage.run]
74
85
  branch = true
@@ -82,23 +93,23 @@ show_contexts = true
82
93
  addopts = "--cov=src/numbers_parser --cov-report=html --cov-report=term-missing:skip-covered --cov-context=test"
83
94
 
84
95
  [tool.tox]
85
- legacy_tox_ini = """
86
- [tox]
87
- isolated_build = true
88
- envlist = py39, py310, py311, py312, py313
96
+ requires = ["tox>=4.0"]
97
+ envlist = ["py39", "py310", "py311", "py312", "py313"]
98
+
89
99
  [testenv]
90
- deps =
91
- pytest
92
- pytest-check
93
- pytest-console-scripts
94
- pytest-cov
95
- pytest-xdist
96
- python-magic
97
- psutil
98
- roman
99
- commands =
100
- pytest tests/ --import-mode importlib -n logical --no-cov
101
- """
100
+ deps = [
101
+ "pytest",
102
+ "pytest-check",
103
+ "pytest-console-scripts",
104
+ "pytest-cov",
105
+ "pytest-xdist",
106
+ "python-magic",
107
+ "pympler",
108
+ "colorama"
109
+ ]
110
+ commands = [
111
+ "pytest tests/ --import-mode importlib -n logical --no-cov"
112
+ ]
102
113
 
103
114
  [tool.isort]
104
115
  profile = "black"
@@ -115,7 +126,6 @@ exclude = [
115
126
  ]
116
127
  fix = true
117
128
  lint.ignore = [
118
- # "PLR2004", # Allow constant values
119
129
  "T201", # Allow print()
120
130
  # To fix:
121
131
  "ANN001", #Missing type annotation
@@ -144,7 +154,7 @@ lint.ignore = [
144
154
  "D401", #First line of docstring should be in imperative mood
145
155
  "D415", #First line should end with a period, question mark, or exclamation point
146
156
  "E501", #Line too long
147
- "ERA001", #Found commented-out code
157
+ "ERA001", # Remove commented out code
148
158
  "FBT001", #Boolean-typed positional argument in function definition
149
159
  "FBT002", #Boolean default positional argument in function definition
150
160
  "FBT003", #Boolean positional value in function call
@@ -191,3 +201,4 @@ ban-relative-imports = "all"
191
201
  "src/build/protodump.py" = ["PLR2004", "INP001", "PTH", "S110", "N806"]
192
202
  "src/debug/**" = ["INP001"]
193
203
  "tests/**" = ["PLR2004", "S101", "D103", "ANN201", "ANN001"]
204
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,104 @@
1
+ import re
2
+ import sys
3
+ from subprocess import PIPE, Popen
4
+
5
+ # Code pattern in AArch64:
6
+ #
7
+ # mov w8, #325
8
+ # strh w8, [sp, #8]
9
+ # str x23, [sp]
10
+ # adrp x2, 2590 ; 0x14e0000
11
+ # add x2, x2, #3776 ; Objc cfstring ref: @"GETPIVOTDATA"
12
+ #
13
+ # TSCEFunction_GETPIVOTDATA::evaluateWithContext(...
14
+
15
+ # Additional required code pattern in AArch64 for Numbers 14.2:
16
+ #
17
+ # mov w0, #319
18
+ # bl TSCEFormulaCreationMagic::function_3arg(...
19
+ # ;
20
+ # ; Approx. 20 lines
21
+ # ;
22
+ # TSCEFormulaCreationMagic::TEXTBETWEEN(...
23
+
24
+ if len(sys.argv) != 3:
25
+ print(f"Usage: {sys.argv[0]} framework-file output.py", file=sys.stderr)
26
+ sys.exit(1)
27
+
28
+ framework = sys.argv[1]
29
+ output_map = sys.argv[2]
30
+
31
+ if framework.endswith(".s"):
32
+ with open(framework, "rb") as fh:
33
+ disassembly = fh.readlines()
34
+ else:
35
+ objdump = Popen( # noqa: S603
36
+ [ # noqa: S607
37
+ "objdump",
38
+ "--disassemble",
39
+ "--no-addresses",
40
+ "--no-print-imm-hex",
41
+ "--no-show-raw-insn",
42
+ "--macho",
43
+ "--objc-meta-data",
44
+ framework,
45
+ ],
46
+ stdout=PIPE,
47
+ )
48
+ cxxfilt = Popen(["c++filt"], stdin=objdump.stdout, stdout=PIPE) # noqa: S603, S607
49
+ objdump.stdout.close()
50
+ disassembly = str(cxxfilt.communicate()[0]).split("\\n")
51
+
52
+ arg = None
53
+ line_count = 0
54
+ tsce_functions = {}
55
+ function_refs = {}
56
+
57
+ previous_line = ""
58
+ for line in disassembly:
59
+ line = str(line).replace("\\t", " ") # noqa: PLW2901
60
+ if m := re.search(r"mov *w8, #(\d+)", line):
61
+ arg = m.group(1)
62
+ line_count = 0
63
+ continue
64
+
65
+ if arg is not None:
66
+ line_count += 1
67
+ if line_count > 30:
68
+ arg = None
69
+ line_count = 0
70
+
71
+ if m := re.search(r'x2, x2.*Objc cfstring ref: @"([A-Z0-9\.]+)"', line):
72
+ if arg is not None and line_count <= 8:
73
+ func = m.group(1).replace("_", ".")
74
+ print(f"Found cstring {func} = {arg}")
75
+ function_refs[func] = arg
76
+ arg = None
77
+ line_count = 0
78
+ elif m := re.search(r"bl *TSCEFormulaCreationMagic::function_3arg\(", line):
79
+ if m := re.search(r"mov *w0, #(\d+)", previous_line):
80
+ arg = m.group(1)
81
+ line_count = 0
82
+ elif (m := re.search(r"TSCEFormulaCreationMagic::(\w+)\(", line)) and arg is not None:
83
+ func = m.group(1).replace("_", ".")
84
+ print(f"Found TSCEFormulaCreationMagic {func} = {arg}")
85
+ function_refs[func] = arg
86
+ arg = None
87
+ line_count = 0
88
+ elif m := re.search(r"TSCEFunction_(\w+)::evaluateWithContext", line):
89
+ func = m.group(1).replace("_", ".")
90
+ print(f"Found TSCEFunction {func}")
91
+ tsce_functions[func] = True
92
+
93
+ previous_line = line
94
+
95
+ function_refs = dict(sorted(function_refs.items(), key=lambda x: int(x[1])))
96
+ with open(output_map, "w") as fh:
97
+ fh.write("FUNCTION_MAP = {\n")
98
+ if "ABS" not in function_refs:
99
+ fh.write(' 1: "ABS",\n')
100
+ for func_name, func_id in function_refs.items():
101
+ if func in tsce_functions:
102
+ fh.write(f' {func_id}: "{func_name}",\n')
103
+
104
+ fh.write("}\n")
@@ -0,0 +1,102 @@
1
+ """
2
+ lldb script to dump TSPersistence registry mapping from iWork apps.
3
+
4
+ Licensed under the MIT license.
5
+
6
+ Copyright 2020-2022 Peter Sobot
7
+ Copyright 2022 Jon Connell
8
+ Copyright 2022 SheetJS LLC
9
+ """
10
+
11
+ import json
12
+ import os
13
+ import sys
14
+
15
+ import lldb
16
+
17
+ from debug.lldbutil import print_stacktrace
18
+
19
+ if len(sys.argv) != 3:
20
+ msg = f"Usage: {sys.argv[0]} exe-file output.json"
21
+ raise (ValueError(msg))
22
+
23
+ exe = sys.argv[1]
24
+ output = sys.argv[2]
25
+
26
+ debugger = lldb.SBDebugger.Create()
27
+ debugger.SetAsync(False)
28
+ target = debugger.CreateTargetWithFileAndArch(exe, None)
29
+
30
+ # # Note: original script also created breakpoints on _handleAEOpenEvent
31
+ # # but that is too early in Numbers 12.1
32
+ # target.BreakpointCreateByName("-[NSApplication _sendFinishLaunchingNotification]")
33
+ # target.BreakpointCreateByName("-[NSApplication _crashOnException:]")
34
+
35
+ # # Note: original script skipped [CKContainer containerWithIdentifier:]
36
+ # target.BreakpointCreateByRegex("CloudKit")
37
+
38
+ target.BreakpointCreateByName("_sendFinishLaunchingNotification")
39
+ target.BreakpointCreateByName("_handleAEOpenEvent:")
40
+ # To get around the fact that we don't have iCloud entitlements when running re-signed code,
41
+ # let's break in the CloudKit code and early exit the function before it can raise an exception:
42
+ target.BreakpointCreateByName("[CKContainer containerWithIdentifier:]")
43
+ # In later Keynote versions, 'containerWithIdentifier' isn't called directly, but we can break on similar methods:
44
+ # Note: this __lldb_unnamed_symbol hack was determined by painstaking experimentation. It will break again for sure.
45
+ target.BreakpointCreateByRegex("___lldb_unnamed_symbol[0-9]+", "CloudKit")
46
+
47
+
48
+ process = target.LaunchSimple(None, None, os.getcwd())
49
+ if not process:
50
+ raise ValueError("Failed to launch process: " + exe)
51
+
52
+ if process.GetState() == lldb.eStateExited:
53
+ msg = f"LLDB was unable to stop process! {process}"
54
+ raise ValueError(msg)
55
+
56
+ try:
57
+ while process.GetState() == lldb.eStateStopped:
58
+ thread = process.GetThreadAtIndex(0)
59
+ frame = thread.GetSelectedFrame()
60
+ if frame.name == "-[NSApplication _crashOnException:]":
61
+ msg = f"Process crashed at {frame.name}"
62
+ raise ValueError(msg)
63
+
64
+ stop_reason = thread.GetStopReason()
65
+
66
+ if stop_reason == lldb.eStopReasonException:
67
+ print_stacktrace(thread)
68
+ function = frame.GetFunction()
69
+ function_or_symbol = function if function else frame.GetSymbol()
70
+ msg = f"Exception at {frame.name}"
71
+ raise ValueError(msg)
72
+ if stop_reason != lldb.eStopReasonBreakpoint:
73
+ process.Continue()
74
+ continue
75
+ if frame.name[-8:] == "CloudKit":
76
+ thread.ReturnFromFrame(
77
+ thread.GetSelectedFrame(),
78
+ lldb.SBValue().CreateValueFromExpression("0", ""),
79
+ )
80
+ process.Continue()
81
+ elif frame.name == "-[NSApplication _sendFinishLaunchingNotification]":
82
+ registry = frame.EvaluateExpression("[TSPRegistry sharedRegistry]")
83
+ error = registry.GetError()
84
+ if error.fail or registry.description is None:
85
+ continue
86
+ # raise (ValueError("Failed to extract registry"))
87
+ split = [
88
+ x.strip().split(" -> ")
89
+ for x in registry.description.split("{")[1].split("}")[0].split("\n")
90
+ if x.strip()
91
+ ]
92
+ json_str = json.dumps(
93
+ dict(sorted([(int(a), b.split(" ")[-1]) for a, b in split if "null" not in b])),
94
+ indent=2,
95
+ )
96
+ with open(output, "w") as fh:
97
+ fh.write(json_str)
98
+ break
99
+ else:
100
+ process.Continue()
101
+ finally:
102
+ process.Kill()
@@ -0,0 +1,22 @@
1
+ import sys
2
+
3
+ import Cocoa
4
+
5
+ if len(sys.argv) != 2:
6
+ msg = f"Usage: {sys.argv[0]} fontmap.py"
7
+ raise (ValueError(msg))
8
+
9
+ mapping_py = sys.argv[1]
10
+
11
+ with open(mapping_py, "w") as fh:
12
+ manager = Cocoa.NSFontManager.sharedFontManager()
13
+ font_families = list(manager.availableFontFamilies())
14
+
15
+ print("FONT_NAME_TO_FAMILY = {", file=fh)
16
+ for family in sorted(font_families):
17
+ fonts = manager.availableMembersOfFontFamily_(family)
18
+ for font in fonts:
19
+ print(f' "{font[0]}": "{family}",', file=fh)
20
+ print(' "Calibri": "Calibri",', file=fh)
21
+ print(' "Cambria": "Cambria",', file=fh)
22
+ print("}", file=fh)
@@ -0,0 +1,70 @@
1
+ import json
2
+ import os
3
+ import sys
4
+
5
+ if len(sys.argv) != 3:
6
+ msg = f"Usage: {sys.argv[0]} mapping.json mapping.py"
7
+ raise (ValueError(msg))
8
+
9
+ mapping_json = sys.argv[1]
10
+ mapping_py = sys.argv[2]
11
+
12
+ modules = []
13
+ module_import_output = ""
14
+ module_files_output = ""
15
+ mapping_output = ""
16
+ for filename in os.listdir("src/numbers_parser/generated"):
17
+ if "pb2" in filename:
18
+ module = filename.replace("_pb2.py", "")
19
+ modules.append(module)
20
+ module_import_output += f"from numbers_parser.generated import {module}_pb2 as {module}\n"
21
+ module_files_output += f" {module},\n"
22
+
23
+ with open(mapping_json) as fh:
24
+ mappings = json.load(fh)
25
+
26
+ for index, symbol in sorted(mappings.items(), key=lambda x: int(x[0])):
27
+ mapping_output += f' "{index}": "{symbol}",\n'
28
+
29
+ OUTPUT_CODE = f"""{module_import_output.strip()}
30
+
31
+ PROTO_FILES = [
32
+ {module_files_output.strip()}
33
+ ]
34
+
35
+ TSPRegistryMapping = {{
36
+ {mapping_output.strip()}
37
+ }}
38
+
39
+
40
+ def compute_maps():
41
+ name_class_map = {{}}
42
+
43
+ def add_nested_types(message_type):
44
+ for name in dict(message_type.DESCRIPTOR.nested_types_by_name):
45
+ child_type = getattr(message_type, name)
46
+ name_class_map[child_type.DESCRIPTOR.full_name] = child_type
47
+ add_nested_types(child_type)
48
+
49
+ for file in PROTO_FILES:
50
+ for message_name in dict(file.DESCRIPTOR.message_types_by_name):
51
+ message_type = getattr(file, message_name)
52
+ name_class_map[message_type.DESCRIPTOR.full_name] = message_type
53
+ add_nested_types(message_type)
54
+
55
+ id_name_map = {{}}
56
+ name_id_map = {{}}
57
+ for k, v in list(TSPRegistryMapping.items()):
58
+ if v in name_class_map: # pragma: no branch
59
+ id_name_map[int(k)] = name_class_map[v]
60
+ if v not in name_id_map:
61
+ name_id_map[v] = int(k)
62
+
63
+ return name_class_map, id_name_map, name_id_map
64
+
65
+
66
+ NAME_CLASS_MAP, ID_NAME_MAP, NAME_ID_MAP = compute_maps()
67
+ """
68
+
69
+ with open(mapping_py, "w") as fh:
70
+ fh.write(OUTPUT_CODE)