partis-pyproj 0.1.4__tar.gz → 0.1.5__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 (125) hide show
  1. partis_pyproj-0.1.5/PKG-INFO +500 -0
  2. partis_pyproj-0.1.5/README.md +475 -0
  3. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/pyproject.toml +26 -63
  4. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/__init__.py +9 -1
  5. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/_legacy_setup.py +11 -11
  6. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/_nonprintable.py +4 -3
  7. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/backend.py +44 -37
  8. partis_pyproj-0.1.5/src/pyproj/builder/builder.py +351 -0
  9. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/builder/cargo.py +2 -2
  10. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/builder/cmake.py +9 -15
  11. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/builder/meson.py +5 -13
  12. partis_pyproj-0.1.5/src/pyproj/builder/process.py +42 -0
  13. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/dist_file/__init__.py +1 -1
  14. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/dist_file/dist_base.py +75 -86
  15. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/dist_file/dist_binary.py +6 -24
  16. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/dist_file/dist_copy.py +7 -18
  17. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/dist_file/dist_source.py +4 -21
  18. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/dist_file/dist_targz.py +5 -12
  19. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/dist_file/dist_zip.py +5 -14
  20. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/file.py +2 -1
  21. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/legacy.py +3 -2
  22. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/load_module.py +7 -6
  23. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/norms.py +35 -31
  24. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/path/__init__.py +2 -1
  25. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/path/match.py +42 -35
  26. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/path/pattern.py +60 -54
  27. partis_pyproj-0.1.5/src/pyproj/path/utils.py +94 -0
  28. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/pep.py +36 -35
  29. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/pkginfo.py +7 -16
  30. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/pptoml.py +125 -120
  31. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/pyproj.py +39 -36
  32. partis_pyproj-0.1.5/src/pyproj/template.py +229 -0
  33. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/validate.py +273 -268
  34. partis_pyproj-0.1.4/PKG-INFO +0 -53
  35. partis_pyproj-0.1.4/README.rst +0 -4
  36. partis_pyproj-0.1.4/doc/__init__.py +0 -0
  37. partis_pyproj-0.1.4/doc/__main__.py +0 -16
  38. partis_pyproj-0.1.4/doc/conf.py +0 -13
  39. partis_pyproj-0.1.4/doc/glossary.rst +0 -12
  40. partis_pyproj-0.1.4/doc/index.rst +0 -553
  41. partis_pyproj-0.1.4/doc/src/backend.rst +0 -4
  42. partis_pyproj-0.1.4/doc/src/builder.rst +0 -9
  43. partis_pyproj-0.1.4/doc/src/dist_file.rst +0 -34
  44. partis_pyproj-0.1.4/doc/src/index.rst +0 -17
  45. partis_pyproj-0.1.4/doc/src/load_module.rst +0 -4
  46. partis_pyproj-0.1.4/doc/src/norms.rst +0 -4
  47. partis_pyproj-0.1.4/doc/src/path.rst +0 -8
  48. partis_pyproj-0.1.4/doc/src/pep.rst +0 -4
  49. partis_pyproj-0.1.4/doc/src/pkginfo.rst +0 -27
  50. partis_pyproj-0.1.4/doc/src/pptoml.rst +0 -4
  51. partis_pyproj-0.1.4/doc/src/pyproj.rst +0 -4
  52. partis_pyproj-0.1.4/doc/src/validate.rst +0 -4
  53. partis_pyproj-0.1.4/setup.py +0 -211
  54. partis_pyproj-0.1.4/src/pyproj/builder/builder.py +0 -267
  55. partis_pyproj-0.1.4/src/pyproj/builder/process.py +0 -75
  56. partis_pyproj-0.1.4/src/pyproj/path/utils.py +0 -40
  57. partis_pyproj-0.1.4/test/__init__.py +0 -0
  58. partis_pyproj-0.1.4/test/__main__.py +0 -32
  59. partis_pyproj-0.1.4/test/noxfile.py +0 -122
  60. partis_pyproj-0.1.4/test/pkg_bad_1/pyproject.toml +0 -69
  61. partis_pyproj-0.1.4/test/pkg_bad_1/src/test_pkg/pure_mod/pure_mod.py +0 -0
  62. partis_pyproj-0.1.4/test/pkg_bad_1/src/test_pkg/sub_mod/sub_sub_mod/bad_file.py +0 -0
  63. partis_pyproj-0.1.4/test/pkg_bad_1/src/test_pkg/sub_mod/sub_sub_mod/good_file.py +0 -0
  64. partis_pyproj-0.1.4/test/pkg_bad_2/pkgaux/__init__.py +0 -15
  65. partis_pyproj-0.1.4/test/pkg_bad_2/pyproject.toml +0 -69
  66. partis_pyproj-0.1.4/test/pkg_bad_2/src/test_pkg/pure_mod/pure_mod.py +0 -0
  67. partis_pyproj-0.1.4/test/pkg_bad_2/src/test_pkg/sub_mod/sub_sub_mod/bad_file.py +0 -0
  68. partis_pyproj-0.1.4/test/pkg_bad_2/src/test_pkg/sub_mod/sub_sub_mod/good_file.py +0 -0
  69. partis_pyproj-0.1.4/test/pkg_bad_3/pyproject.toml +0 -27
  70. partis_pyproj-0.1.4/test/pkg_bad_4/pkgaux/__init__.py +0 -23
  71. partis_pyproj-0.1.4/test/pkg_bad_4/pyproject.toml +0 -58
  72. partis_pyproj-0.1.4/test/pkg_bad_4/src/test_pkg/pure_mod/pure_mod.py +0 -0
  73. partis_pyproj-0.1.4/test/pkg_bad_4/src/test_pkg/sub_mod/sub_sub_mod/bad_file.py +0 -0
  74. partis_pyproj-0.1.4/test/pkg_bad_4/src/test_pkg/sub_mod/sub_sub_mod/good_file.py +0 -0
  75. partis_pyproj-0.1.4/test/pkg_bad_5/pkgaux/__init__.py +0 -15
  76. partis_pyproj-0.1.4/test/pkg_bad_5/pyproject.toml +0 -46
  77. partis_pyproj-0.1.4/test/pkg_bad_5/src/test_pkg/pure_mod/pure_mod.py +0 -0
  78. partis_pyproj-0.1.4/test/pkg_bad_5/src/test_pkg/sub_mod/sub_sub_mod/bad_file.py +0 -0
  79. partis_pyproj-0.1.4/test/pkg_bad_5/src/test_pkg/sub_mod/sub_sub_mod/good_file.py +0 -0
  80. partis_pyproj-0.1.4/test/pkg_base/pkgaux/__init__.py +0 -23
  81. partis_pyproj-0.1.4/test/pkg_base/pyproject.toml +0 -69
  82. partis_pyproj-0.1.4/test/pkg_base/src/test_pkg/pure_mod/pure_mod.py +0 -0
  83. partis_pyproj-0.1.4/test/pkg_base/src/test_pkg/sub_mod/bad_file.py +0 -0
  84. partis_pyproj-0.1.4/test/pkg_base/src/test_pkg/sub_mod/good_file.py +0 -0
  85. partis_pyproj-0.1.4/test/pkg_base/src/test_pkg/sub_mod/sub_sub_mod/bad_file.py +0 -0
  86. partis_pyproj-0.1.4/test/pkg_base/src/test_pkg/sub_mod/sub_sub_mod/good_file.py +0 -0
  87. partis_pyproj-0.1.4/test/pkg_cmake_1/CMakeLists.txt +0 -15
  88. partis_pyproj-0.1.4/test/pkg_cmake_1/pyproject.toml +0 -56
  89. partis_pyproj-0.1.4/test/pkg_cmake_1/src/test_pkg/CMakeLists.txt +0 -11
  90. partis_pyproj-0.1.4/test/pkg_cmake_1/src/test_pkg/plat_mod.pyx +0 -4
  91. partis_pyproj-0.1.4/test/pkg_cmake_1/src/test_pkg/pure_mod.py +0 -0
  92. partis_pyproj-0.1.4/test/pkg_meson_1/meson.build +0 -24
  93. partis_pyproj-0.1.4/test/pkg_meson_1/meson_options.txt +0 -11
  94. partis_pyproj-0.1.4/test/pkg_meson_1/pyproject.toml +0 -63
  95. partis_pyproj-0.1.4/test/pkg_meson_1/src/test_pkg/plat_mod.pyx +0 -4
  96. partis_pyproj-0.1.4/test/pkg_meson_1/src/test_pkg/pure_mod.py +0 -0
  97. partis_pyproj-0.1.4/test/pkg_meson_2/meson.build +0 -24
  98. partis_pyproj-0.1.4/test/pkg_meson_2/meson_options.txt +0 -11
  99. partis_pyproj-0.1.4/test/pkg_meson_2/pyproject.toml +0 -63
  100. partis_pyproj-0.1.4/test/pkg_meson_2/src/test_pkg/plat_mod.pyx +0 -4
  101. partis_pyproj-0.1.4/test/pkg_meson_2/src/test_pkg/pure_mod.py +0 -0
  102. partis_pyproj-0.1.4/test/pkg_meson_bad_1/bad_link/meson.build +0 -24
  103. partis_pyproj-0.1.4/test/pkg_meson_bad_1/bad_link/meson_options.txt +0 -11
  104. partis_pyproj-0.1.4/test/pkg_meson_bad_1/bad_link/pyproject.toml +0 -63
  105. partis_pyproj-0.1.4/test/pkg_meson_bad_1/bad_link/src/test_pkg/plat_mod.pyx +0 -4
  106. partis_pyproj-0.1.4/test/pkg_meson_bad_1/bad_link/src/test_pkg/pure_mod.py +0 -0
  107. partis_pyproj-0.1.4/test/pkg_meson_bad_1/meson.build +0 -24
  108. partis_pyproj-0.1.4/test/pkg_meson_bad_1/meson_options.txt +0 -11
  109. partis_pyproj-0.1.4/test/pkg_meson_bad_1/pyproject.toml +0 -65
  110. partis_pyproj-0.1.4/test/pkg_meson_bad_1/src/test_pkg/plat_mod.pyx +0 -4
  111. partis_pyproj-0.1.4/test/pkg_meson_bad_1/src/test_pkg/pure_mod.py +0 -0
  112. partis_pyproj-0.1.4/test/pkg_min/pure_mod.py +0 -0
  113. partis_pyproj-0.1.4/test/pkg_min/pyproject.toml +0 -26
  114. partis_pyproj-0.1.4/test/sitecustom/partis-sitecustom.pth +0 -26
  115. partis_pyproj-0.1.4/test/sitecustom/pyproject.toml +0 -26
  116. partis_pyproj-0.1.4/test/test_00_validate.py +0 -498
  117. partis_pyproj-0.1.4/test/test_01_norms.py +0 -520
  118. partis_pyproj-0.1.4/test/test_02_path.py +0 -302
  119. partis_pyproj-0.1.4/test/test_03_dist.py +0 -315
  120. partis_pyproj-0.1.4/test/test_04_load_module.py +0 -25
  121. partis_pyproj-0.1.4/test/test_05_pkginfo.py +0 -208
  122. partis_pyproj-0.1.4/test/test_06_pyproj.py +0 -284
  123. partis_pyproj-0.1.4/test/test_07_backend.py +0 -57
  124. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/LICENSE.txt +0 -0
  125. {partis_pyproj-0.1.4 → partis_pyproj-0.1.5}/src/pyproj/builder/__init__.py +0 -0
@@ -0,0 +1,500 @@
1
+ Metadata-Version: 2.1
2
+ Name: partis-pyproj
3
+ Version: 0.1.5
4
+ Requires-Python: >=3.8
5
+ Author-email: "Nanohmics Inc." <software.support@nanohmics.com>
6
+ Maintainer-email: "Nanohmics Inc." <software.support@nanohmics.com>
7
+ Summary: Minimal set of Python project utilities (PEP-517/621)
8
+ License-File: LICENSE.txt
9
+ Classifier: Topic :: Software Development :: Build Tools
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: Operating System :: Microsoft :: Windows
12
+ Classifier: Programming Language :: Python
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: BSD License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Provides-Extra: meson
18
+ Provides-Extra: cmake
19
+ Requires-Dist: packaging>=24.2
20
+ Requires-Dist: tomli>=2.0.1
21
+ Requires-Dist: meson>=0.61.3; extra == "meson"
22
+ Requires-Dist: ninja>=1.10.2.3; extra == "meson"
23
+ Requires-Dist: ninja>=1.10.2.3; extra == "cmake"
24
+ Requires-Dist: cmake>=3.24.3; extra == "cmake"
25
+ Description-Content-Type: text/markdown
26
+
27
+ [![tests](https://github.com/kcdodd/partis-pyproj/actions/workflows/tests.yaml/badge.svg)](https://github.com/kcdodd/partis-pyproj/actions/workflows/tests.yaml)
28
+
29
+ The ``partis.pyproj`` package aims to be a minimal and
30
+ transparent implementation of a [PEP-517](https://www.python.org/dev/peps/pep-0517) build back-end.
31
+ The guiding principles adopted for ``partis.pyproj`` are:
32
+
33
+ * Stateless and agnostic to project structure and management, focused on the
34
+ stages of preparing a distribution.
35
+ * Avoid inspecting or inferring "desired behavior" from the contents of the package
36
+ being distributed / installed, and provide as much control as possible over the
37
+ entire process.
38
+ * All configuration of ``partis.pyproj`` contained in ``pyproject.toml``
39
+ * A distribution is simply a collection of files,
40
+ plus package meta-data for either source or binary (wheel) distribution formats.
41
+
42
+ The process of building a source or binary distribution is broken down into
43
+ three general stages:
44
+
45
+ - **prepare** - Actions required to start the process of creating a distribution.
46
+ The 'prep' may be any custom function the developer wants to occur before files
47
+ are copied into the distribution, such as filling in dynamic metadata.
48
+ - **build** - If needed, running one or more build stages using third-party
49
+ or custom methods. ``partis.pyproj`` provides some standard structure to this
50
+ configuration, but otherwise avoids taking on the responsibility of a full build system.
51
+ - **copy** - Copy files into the distribution.
52
+
53
+
54
+ The sequence of actions for a distribution is roughly:
55
+
56
+ - `tool.pyproj.prep`: Run before anything else, used to fill in dynamic metadata or
57
+ update to `build_requires` list for binary distributions (front-end to install build requirements).
58
+ - `tool.pyproj.dist.prep`: Run for both source and binary distributions to prepare
59
+ or configure initial files.
60
+ - `tool.pyproj.dist.source.prep`: Runs before copying files to a source distribution.
61
+ - `tool.pyproj.targets`: Run build targets, in order, where `enabled` evaluates to true
62
+ (conditions based on [environment markers](https://packaging.pypa.io/en/stable/markers.html)).
63
+ - `tool.pyproj.dist.binary.prep`: Run before copying files to a binary distribution
64
+ (after all enabled build targets complete).
65
+ Can also customize compatibility tags for the binary distribution as per [PEP 425](https://peps.python.org/pep-0425/).
66
+
67
+ ### Copy Operations
68
+
69
+ The majority of ``partis.pyproj`` is devoted to copying files into a distribution.
70
+ This logic is the most complicated, but derives from a combination of existing
71
+ formats and behaviors.
72
+
73
+ **Source and destination**
74
+
75
+ * Each item listed in a `copy` is treated like `copyfile` or `copytree`, depending on whether the `src` is a file or a directory.
76
+ * All `src` must exist within the root of the project, any external or generated
77
+ files must be prepared before the copy operation.
78
+ * If `src` is a directory, all files are copied recursively unless they
79
+ match an ignore pattern for that distribution type.
80
+ * If an item is a single path, it is expanded as ``dst = src``.
81
+ * `dst` is relative, specifically depending on whether it is a source or binary (wheel) distribution and which install scheme is desired (`purelib`, `platlib`, etc.).
82
+ * Destination file paths are constructed from matched source paths roughly equivalent
83
+ to `{scheme}/dst/match.relative_to(src)`.
84
+
85
+ **Include patterns**
86
+
87
+ * An `include` list is used to filter files or directories to be copied, expanded
88
+ to zero or more matches relative to `src`.
89
+ * `glob` follows the format of [Path.glob](https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob), with recursion.
90
+ * `rematch` may further discriminate files (already matched by `glob`) using [Regular Expression Syntax](https://docs.python.org/3/library/re.html#regular-expression-syntax).
91
+ * `replace` can change destination filenames using
92
+ [Format String Syntax](https://docs.python.org/3/library/string.html#format-string-syntax), with values supplied by any groups defined in `rematch`.
93
+
94
+ **Ignore patterns**
95
+
96
+ * An `ignore` list follows the format of [git-ignore](https://git-scm.com/docs/gitignore#_pattern_format).
97
+ * Individual *files* explicitly listed as a `src` will be copied, even if it
98
+ matches one of the `ignore` patterns.
99
+ * The `ignore` patterns may be specified at different levels of distributions in
100
+ ``tool.pyproj.dist``, ``tool.pyproj.dist.source``, ``tool.pyproj.dist.binary``,or individual copy operations ``{ src = '...', dst = '...', ignore = [...] }``.
101
+ The ignore patterns are inherited at each level of specificity.
102
+ * If an ignore pattern **does not contain** any path separators, it is matched to
103
+ the **base-name** of every file or directory being considered (E.G. `foo` is
104
+ equivalent to ``**/foo``).
105
+ * If an ignore pattern **does contain** a path separator, then it is matched to the
106
+ **full path** relative to either the project root (for distribution-level ignores)
107
+ or `src` (for copy-level ignores).
108
+
109
+
110
+ Individual copy operations have the following structure, but there are also
111
+ shorthands where the rest are default. A semi-formal description of this configuration follows:
112
+
113
+ ```
114
+ copy: copy_item | array{copy_item}
115
+ copy_item: PATH | table{"src =" PATH, "dst =" PATH?, "include =" include?, "ignore =" ignore?}
116
+ include: GLOB | array{include_item}
117
+ include_item: GLOB | table{"glob =" GLOB, "rematch =" REGEX?, "replace =" FORMAT?}
118
+ ignore: IGNORE | array{IGNORE}
119
+
120
+ PATH: < POSIX path, implicitly relative to project root >
121
+ GLOB: < https://docs.python.org/3/library/glob.html#glob.glob >
122
+ REGEX: < https://docs.python.org/3/library/re.html#regular-expression-syntax >
123
+ FORMAT: < https://docs.python.org/3/library/string.html#formatstrings >
124
+ IGNORE: < https://git-scm.com/docs/gitignore#_pattern_format >
125
+ ```
126
+
127
+ **Example**
128
+
129
+ ```toml
130
+ # pyproject.toml
131
+ [tool.pyproj.dist]
132
+ ignore = [
133
+ '__pycache__',
134
+ 'doc/_build' ]
135
+
136
+ [tool.pyproj.dist.source]
137
+
138
+ ignore = [
139
+ '*.so' ]
140
+
141
+ copy = [
142
+ 'src',
143
+ 'doc',
144
+ 'pyproject.toml' ]
145
+
146
+ [[tool.pyproj.dist.binary.purelib.copy]]
147
+ src = 'src/my_project'
148
+ include = '**/*.py'
149
+ dst = 'my_project'
150
+ ignore = [
151
+ 'bad_file.py',
152
+ './config_file.py']
153
+
154
+ [[tool.pyproj.dist.binary.platlib.copy]]
155
+ src = 'src/my_project'
156
+ include = '**/*.so'
157
+ dst = 'my_project'
158
+ ```
159
+
160
+
161
+ ##### Source Distribution (``.tar.gz``)
162
+
163
+ | Result | File Path |
164
+ |--------|-----------|
165
+ | **Included** | ``pyproject.toml`` |
166
+ | **Included** | ``doc/index.rst`` |
167
+ | **Included** | ``src/my_project/__init__.py`` |
168
+ | **Included** | ``src/doc/_build`` |
169
+ | *Ignored* | ``doc/_build`` |
170
+ | *Ignored* | ``doc/__pycache__`` |
171
+ | *Ignored* | `__pycache__` |
172
+ | *Ignored* | ``src/__pycache__`` |
173
+ | *Ignored* | ``src/my_project/mylib.so`` |
174
+
175
+
176
+ ##### Binary Distribution (``.whl``)
177
+
178
+ | Result | File Path |
179
+ |--------|-----------|
180
+ | **Included** | ``src/my_project/__init__.py`` |
181
+ | **Included** | ``src/my_project/sub_dir/__init__.py`` |
182
+ | **Included** | ``src/my_project/sub_dir/config_file.py`` |
183
+ | **Included** | ``src/my_project/mylib.so`` |
184
+ | *Ignored* | ``src/my_project/bad_file.py`` |
185
+ | *Ignored* | ``src/my_project/config_file.py`` |
186
+ | *Ignored* | ``src/my_project/sub_dir/bad_file.py`` |
187
+
188
+
189
+ Preparation Hooks
190
+ -----------------
191
+
192
+ The backend provides a mechanism to perform an arbitrary operation before any
193
+ files are copied into either the source or binary distribution:
194
+
195
+ Each hook must be a python module (a directory with an
196
+ ``__init__.py`` file), either directly importable or relative to the 'pyproject.toml'.
197
+ The hook is specified according to the `entry_points` specification, and
198
+ must resolve to a function that takes the instance of the build system and
199
+ a logger.
200
+ Keyword arguments may also be defined to be passed to the function,
201
+ configured in the same section of the 'pyproject.toml'.
202
+
203
+ ```toml
204
+ # pyproject.toml
205
+ [tool.pyproj.dist.binary.prep]
206
+ # hook defined in a python module
207
+ entry = "a_custom_prep_module:a_prep_function"
208
+
209
+ [tool.pyproj.dist.binary.prep.kwargs]
210
+ # define keyword argument values to be passed to the pre-processing hook
211
+ a_custom_argument = 'some value'
212
+ ```
213
+
214
+ This will be treated by the backend **equivalent to the
215
+ following code** run from the `pyproject.toml` directory:
216
+
217
+ ```python
218
+ # equivalent backend
219
+ import a_custom_prep_module
220
+
221
+ a_custom_prep_module.a_prep_function(
222
+ backend,
223
+ logger,
224
+ a_custom_argument = 'some value' )
225
+ ```
226
+
227
+ > :warning: Only those requirements listed in `build-system.requires`
228
+ > will be importable during `tool.pyproj.prep`, and only those added to
229
+ > `backend.build_requires` will be available in subsequent hooks.
230
+
231
+ Dynamic Metadata
232
+ ----------------
233
+
234
+ As described in [PEP-621](https://www.python.org/dev/peps/pep-0621), field values in the `project` table may be deferred
235
+ to the backend by listing the keys in `project.dynamic`, which must be set by the `tool.pyproj.prep` processing hook.
236
+
237
+ ```toml
238
+ # pyproject.toml
239
+ [project]
240
+ name = "my_pkg"
241
+ dynamic = ["version"]
242
+
243
+ [tool.pyproj.prep]
244
+ entry = "pkgaux:prep"
245
+ ```
246
+
247
+ The hook should set values for all keys of the `project` table listed
248
+ in ``project.dynamic``.
249
+
250
+ ```python
251
+ def prep( backend, logger ):
252
+ backend.project.version = "1.2.3"
253
+ ```
254
+
255
+ #### Build Targets
256
+
257
+ Methods of compiling extensions (or anything else) is delegated to third-party
258
+ build systems specified in the 'pyproject.toml' array ``tool.pyproj.targets``.
259
+ This means that, unlike with setuptools, detailed configuration of the build itself
260
+ would likely be stored in separate files like ``meson.build`` with Meson,
261
+ or ``CMakeLists.txt`` with CMake.
262
+
263
+ In case different options are needed depending on the environment, the `enabled`
264
+ field can be a [PEP-508](https://www.python.org/dev/peps/pep-0508) [environment marker](https://packaging.pypa.io/en/stable/markers.html),
265
+ or can also be set manually (True/False) by an earlier 'prep' stage.
266
+ Each third-party build system is given by the `entry`, which is an entry-point
267
+ to a function that takes in the arguments and options given in the table
268
+ for that build.
269
+
270
+ **standard config**
271
+ ```
272
+ entry: ENTRY_POINT # entry-point specification of builder to use
273
+ work_dir: PATH # directory from which the builder will be run
274
+ src_dir: PATH # directory of source code
275
+ build_dir: PATH # directory for temporary build files (cleaned on exit)
276
+ prefix: PATH # directory which distribution files should be staged (cleaned on exit)
277
+ setup_args: array{STRING} # 3-stage build
278
+ compile_args: array{STRING} # 3-stage build
279
+ install_args: array{STRING} # 3-stage build
280
+ options: table{STRING|BOOL}? # options passed to builder from pyproject.toml
281
+ build_clean: BOOL? # control cleanup (ie for development builds)
282
+ enabled: (BOOL|MARKER)? # environment marker
283
+ ```
284
+
285
+ There are several entry points available as-is:
286
+
287
+ - `partis.pyproj.builder:meson` - Support for [Meson Build system](https://mesonbuild.com/) with the 'extra' ``partis-pyproj[meson]``
288
+ - `partis.pyproj.builder:cmake` - Support for [CMake](https://cmake.org/) with the 'extra' ``partis-pyproj[cmake]``
289
+ - `partis.pyproj.builder:process` - Support for running arbitrary command line executable
290
+
291
+ In this example, the source directory must contain appropriate `meson.build` files,
292
+ since the 'pyproject.toml' configuration only provides a way of running
293
+ ``meson setup`` and ``meson compile``.
294
+ For example:
295
+
296
+ ```toml
297
+ # pyproject.toml
298
+ [[tool.pyproj.targets]]
299
+
300
+ entry = 'partis.pyproj.builder:meson'
301
+
302
+ # location to create temporary build files (optional)
303
+ build_dir = 'tmp/build'
304
+ # location to place final build targets
305
+ prefix = 'tmp/prefix'
306
+
307
+ [tool.pyproj.targets.options]
308
+ # Custom build options (e.g. passing to meson -Dcustom_feature=enabled)
309
+ custom_feature = 'enabled'
310
+
311
+ [tool.pyproj.dist.binary.platlib]
312
+ # binary distribution platform specific install path
313
+ copy = [
314
+ { src = 'tmp/prefix/lib', dst = 'my_project' } ]
315
+ ```
316
+
317
+ The `src_dir` and `prefix` paths are always relative to the project
318
+ root directory, and default to ``src_dir = '.'`` and ``prefix = './build'``.
319
+ Currently these must all be a sub-directory relative to the 'pyproject.toml'
320
+ (e.g. a specified temporary directory).
321
+
322
+ The result should be equivalent to running the following commands in a custom
323
+ defined builder:
324
+
325
+ ```python
326
+
327
+ def custom_builder(
328
+ backend,
329
+ logger: logging.Logger,
330
+ options: dict,
331
+ work_dir: Path,
332
+ src_dir: Path,
333
+ build_dir: Path,
334
+ prefix: Path,
335
+ setup_args: list[str],
336
+ compile_args: list[str],
337
+ install_args: list[str],
338
+ build_clean: bool,
339
+ runner: ProcessRunner):
340
+
341
+ runner.run(['meson', 'setup', *setup_args, '--prefix', prefix, build_dir src_dir])
342
+ runner.run(['meson', 'compile', *compile_args, '-C', build_dir])
343
+ runner.run(['meson', 'install', *install_args, '-C', build_dir])
344
+ ```
345
+
346
+ All files in 'build/lib' are then copied into the binary distribution's 'platlib' install path.
347
+
348
+ A custom 'builder' for the entry-point can also be used, and is simply a callable
349
+ with the correct signature.
350
+
351
+ **Template substitution**
352
+
353
+ The paths and options in build targets may contain template substitutions to more
354
+ easily pass environment-dependent information to the third-party build system.
355
+ The substitution rule is specialized to `partis-pyproj`, but derives from
356
+ Python [Template string](https://docs.python.org/3/library/string.html#template-strings) with additions to support nested identifiers and construction of paths. The commonality is:
357
+
358
+ - `$$` is an escape; it is replaced with a single `$`.
359
+ - `${identifier}` names a substitution placeholder matching a mapping key of "identifier".
360
+
361
+ However, `$identifier` (without braces) is not supported, instead allowing more expressive substitutions.
362
+
363
+ ```
364
+ substitution: "${" (variable|literal|SEP)+ "}"
365
+ variable: IDENTIFIER ("." IDENTIFIER | "[" INTEGER "]")*
366
+ SEP: "/"
367
+ literal: "'" CHAR+ "'"
368
+ IDENTIFIER: < python identifier >
369
+ INTEGER: < integer >
370
+ CHAR: < ascii letter, number (not leading), or underscore >
371
+ ```
372
+
373
+ Variable names can reference most of the content of the original 'pyproject.toml',
374
+ as well as values already substituted in the build target or earlier targets.
375
+ If the substitution contains any separators the result is interpreted as a path, converted to platform-specific filesystem format, and resolved to project directory.
376
+
377
+ **Example**
378
+
379
+ The value of `options.some_option` in the example below would be substituted with a filesystem equivalent path for `{root}/build/something/my_pkg/xyz/abc.so`:
380
+
381
+
382
+ ```toml
383
+ # pyproject.toml
384
+ [project]
385
+ name = "my_pkg"
386
+
387
+ [[tool.pyproj.targets]]
388
+ prefix = "build/something"
389
+ options = {some_option = "${prefix/project.name/'xyz'/'abc.so'}"}
390
+ ```
391
+
392
+ Binary distribution install paths
393
+ ---------------------------------
394
+
395
+ If there are some binary distribution files that need to be installed to a
396
+ location according to a local installation scheme
397
+ these can be specified within sub-tables.
398
+ Available install scheme keys, and **example** corresponding install locations, are:
399
+
400
+ - `purelib` ("pure" library Python path): ``{prefix}/lib/python{X}.{Y}/site-packages/``
401
+ - `platlib` (platform specific Python path): ``{prefix}/lib{platform}/python{X}.{Y}/site-packages/``
402
+ Both `purelib` and `platlib` install to the base 'site-packages'
403
+ directory, so any files copied to these paths should be placed within a
404
+ desired top-level package directory.
405
+
406
+ - `headers` (INCLUDE search paths): ``{prefix}/include/{site}/python{X}.{Y}{abiflags}/{distname}/``
407
+ - `scripts` (executable search path): ``{prefix}/bin/``
408
+ Even though any files added to the `scripts` path will be installed to
409
+ the `bin` directory, there is often an issue with the 'execute' permission
410
+ being set correctly by the installer (e.g. `pip`).
411
+ The only verified way of ensuring an executable in the 'bin' directory is to
412
+ use the ``[project.scripts]`` section to add an entry point that will then
413
+ run the desired executable as a sub-process.
414
+
415
+ - `data` (generic data path): ``{prefix}/``
416
+
417
+ ```toml
418
+ # pyproject.toml
419
+ [tool.pyproj.dist.binary.purelib]
420
+ copy = [
421
+ { src = 'build/my_project.py', dst = 'my_project/my_project.py'} ]
422
+
423
+ [tool.pyproj.dist.binary.platlib]
424
+ copy = [
425
+ { src = 'build/my_project.so', dst = 'my_project/my_project.so'} ]
426
+
427
+ [tool.pyproj.dist.binary.headers]
428
+ copy = [
429
+ { src = 'build/header.hpp', dst = 'header.hpp' } ]
430
+
431
+ [tool.pyproj.dist.binary.scripts]
432
+ copy = [
433
+ { src = 'build/script.py', dst = 'script.py'} ]
434
+
435
+ [tool.pyproj.dist.binary.data]
436
+ copy = [
437
+ { src = 'build/data.dat', dst = 'data.dat' } ]
438
+ ```
439
+
440
+ Config Settings
441
+ ---------------
442
+
443
+ As described in [PEP-517](https://www.python.org/dev/peps/pep-0517),
444
+ an installer front-end may implement support for
445
+ passing additional options to the backend
446
+ (e.g. ``--config-settings`` in `pip`).
447
+ These options may be defined in the ``tool.pyproj.config`` table, which is used
448
+ to validate the allowed options, fill in default values, and cast to
449
+ desired types.
450
+ These settings, updated by any values passed from the front-end installer,
451
+ are available in any processing hook.
452
+ Combined with an entry-point `kwargs`, these can be used to keep all
453
+ conditional dependencies listed in ``pyproject.toml``.
454
+
455
+
456
+ The type is derived from the value parsed from ``pyproject.toml``.
457
+ For example, the value of `3` is parsed as an integer, while ``3.0`` is parsed
458
+ as a float.
459
+ Additionally, the ``tool.pyproj.config`` table may **not** contain nested tables,
460
+ since it must be able to map 1:1 with arguments passed on
461
+ the command line.
462
+ A single-level list may be set as a value to restrict the allowed value to
463
+ one of those in the list, with the first item in the list being used as the
464
+ default value.
465
+
466
+ Boolean values passed to ``--config-settings`` are parsed by comparing to
467
+ string values ``['true', 'True', 'yes', 'y', 'enable', 'enabled']``
468
+ or ``['false', 'False', 'no', 'n', 'disable', 'disabled']``.
469
+
470
+ ```toml
471
+
472
+ [tool.pyproj.config]
473
+ a_cfg_option = false
474
+ another_option = ["foo", "bar"]
475
+
476
+ [tool.pyproj.prep]
477
+ entry = "pkgaux:prep"
478
+ kwargs = { deps = ["additional_build_dep >= 1.2.3"] }
479
+ ```
480
+
481
+ ```python
482
+ # pkgaux/__init__.py
483
+
484
+ def prep( backend, logger, deps ):
485
+
486
+ if backend.config_settings.a_cfg_option:
487
+ backend.build_requires |= set(deps)
488
+
489
+ if backend.config_settings.another_option == 'foo':
490
+ ...
491
+
492
+ elif backend.config_settings.another_option == 'bar':
493
+ ...
494
+ ```
495
+
496
+ In this example, the command
497
+ ``pip install --config-settings a_cfg_option=true ...`` will cause the
498
+ 'additional_build_dep' to be installed before the build occurs.
499
+ The value of `another_option` may be either `foo` or `bar`,
500
+ and all other values will raise an exception before reaching the entry-point.