ZhengyangZhang commited on
Commit
2f00a8b
·
verified ·
1 Parent(s): 9d557fc

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/INSTALLER +1 -0
  2. lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE +279 -0
  3. lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA +123 -0
  4. lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/RECORD +16 -0
  5. lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/WHEEL +4 -0
  6. lib/python3.12/site-packages/fonttools-4.61.1.dist-info/RECORD +689 -0
  7. lib/python3.12/site-packages/fonttools-4.61.1.dist-info/WHEEL +8 -0
  8. lib/python3.12/site-packages/fonttools-4.61.1.dist-info/entry_points.txt +5 -0
  9. lib/python3.12/site-packages/fonttools-4.61.1.dist-info/licenses/LICENSE +21 -0
  10. lib/python3.12/site-packages/fonttools-4.61.1.dist-info/licenses/LICENSE.external +388 -0
  11. lib/python3.12/site-packages/fonttools-4.61.1.dist-info/top_level.txt +1 -0
  12. lib/python3.12/site-packages/gitdb/__init__.py +16 -0
  13. lib/python3.12/site-packages/gitdb/__pycache__/__init__.cpython-312.pyc +0 -0
  14. lib/python3.12/site-packages/gitdb/__pycache__/base.cpython-312.pyc +0 -0
  15. lib/python3.12/site-packages/gitdb/__pycache__/const.cpython-312.pyc +0 -0
  16. lib/python3.12/site-packages/gitdb/__pycache__/exc.cpython-312.pyc +0 -0
  17. lib/python3.12/site-packages/gitdb/__pycache__/fun.cpython-312.pyc +0 -0
  18. lib/python3.12/site-packages/gitdb/__pycache__/pack.cpython-312.pyc +0 -0
  19. lib/python3.12/site-packages/gitdb/__pycache__/stream.cpython-312.pyc +0 -0
  20. lib/python3.12/site-packages/gitdb/__pycache__/typ.cpython-312.pyc +0 -0
  21. lib/python3.12/site-packages/gitdb/__pycache__/util.cpython-312.pyc +0 -0
  22. lib/python3.12/site-packages/gitdb/base.py +315 -0
  23. lib/python3.12/site-packages/gitdb/const.py +4 -0
  24. lib/python3.12/site-packages/gitdb/db/__init__.py +11 -0
  25. lib/python3.12/site-packages/gitdb/db/__pycache__/__init__.cpython-312.pyc +0 -0
  26. lib/python3.12/site-packages/gitdb/db/__pycache__/base.cpython-312.pyc +0 -0
  27. lib/python3.12/site-packages/gitdb/db/__pycache__/git.cpython-312.pyc +0 -0
  28. lib/python3.12/site-packages/gitdb/db/__pycache__/loose.cpython-312.pyc +0 -0
  29. lib/python3.12/site-packages/gitdb/db/__pycache__/mem.cpython-312.pyc +0 -0
  30. lib/python3.12/site-packages/gitdb/db/__pycache__/pack.cpython-312.pyc +0 -0
  31. lib/python3.12/site-packages/gitdb/db/__pycache__/ref.cpython-312.pyc +0 -0
  32. lib/python3.12/site-packages/gitdb/db/base.py +278 -0
  33. lib/python3.12/site-packages/gitdb/db/git.py +85 -0
  34. lib/python3.12/site-packages/gitdb/db/loose.py +268 -0
  35. lib/python3.12/site-packages/gitdb/db/mem.py +110 -0
  36. lib/python3.12/site-packages/gitdb/db/pack.py +206 -0
  37. lib/python3.12/site-packages/gitdb/db/ref.py +82 -0
  38. lib/python3.12/site-packages/gitdb/exc.py +57 -0
  39. lib/python3.12/site-packages/gitdb/fun.py +704 -0
  40. lib/python3.12/site-packages/gitdb/pack.py +1031 -0
  41. lib/python3.12/site-packages/gitdb/stream.py +730 -0
  42. lib/python3.12/site-packages/gitdb/test/__init__.py +4 -0
  43. lib/python3.12/site-packages/gitdb/test/__pycache__/__init__.cpython-312.pyc +0 -0
  44. lib/python3.12/site-packages/gitdb/test/__pycache__/lib.cpython-312.pyc +0 -0
  45. lib/python3.12/site-packages/gitdb/test/__pycache__/test_base.cpython-312.pyc +0 -0
  46. lib/python3.12/site-packages/gitdb/test/__pycache__/test_example.cpython-312.pyc +0 -0
  47. lib/python3.12/site-packages/gitdb/test/__pycache__/test_pack.cpython-312.pyc +0 -0
  48. lib/python3.12/site-packages/gitdb/test/__pycache__/test_stream.cpython-312.pyc +0 -0
  49. lib/python3.12/site-packages/gitdb/test/__pycache__/test_util.cpython-312.pyc +0 -0
  50. lib/python3.12/site-packages/gitdb/test/lib.py +192 -0
lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ A. HISTORY OF THE SOFTWARE
2
+ ==========================
3
+
4
+ Python was created in the early 1990s by Guido van Rossum at Stichting
5
+ Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands
6
+ as a successor of a language called ABC. Guido remains Python's
7
+ principal author, although it includes many contributions from others.
8
+
9
+ In 1995, Guido continued his work on Python at the Corporation for
10
+ National Research Initiatives (CNRI, see https://www.cnri.reston.va.us)
11
+ in Reston, Virginia where he released several versions of the
12
+ software.
13
+
14
+ In May 2000, Guido and the Python core development team moved to
15
+ BeOpen.com to form the BeOpen PythonLabs team. In October of the same
16
+ year, the PythonLabs team moved to Digital Creations, which became
17
+ Zope Corporation. In 2001, the Python Software Foundation (PSF, see
18
+ https://www.python.org/psf/) was formed, a non-profit organization
19
+ created specifically to own Python-related Intellectual Property.
20
+ Zope Corporation was a sponsoring member of the PSF.
21
+
22
+ All Python releases are Open Source (see https://opensource.org for
23
+ the Open Source Definition). Historically, most, but not all, Python
24
+ releases have also been GPL-compatible; the table below summarizes
25
+ the various releases.
26
+
27
+ Release Derived Year Owner GPL-
28
+ from compatible? (1)
29
+
30
+ 0.9.0 thru 1.2 1991-1995 CWI yes
31
+ 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
32
+ 1.6 1.5.2 2000 CNRI no
33
+ 2.0 1.6 2000 BeOpen.com no
34
+ 1.6.1 1.6 2001 CNRI yes (2)
35
+ 2.1 2.0+1.6.1 2001 PSF no
36
+ 2.0.1 2.0+1.6.1 2001 PSF yes
37
+ 2.1.1 2.1+2.0.1 2001 PSF yes
38
+ 2.1.2 2.1.1 2002 PSF yes
39
+ 2.1.3 2.1.2 2002 PSF yes
40
+ 2.2 and above 2.1.1 2001-now PSF yes
41
+
42
+ Footnotes:
43
+
44
+ (1) GPL-compatible doesn't mean that we're distributing Python under
45
+ the GPL. All Python licenses, unlike the GPL, let you distribute
46
+ a modified version without making your changes open source. The
47
+ GPL-compatible licenses make it possible to combine Python with
48
+ other software that is released under the GPL; the others don't.
49
+
50
+ (2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
51
+ because its license has a choice of law clause. According to
52
+ CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
53
+ is "not incompatible" with the GPL.
54
+
55
+ Thanks to the many outside volunteers who have worked under Guido's
56
+ direction to make these releases possible.
57
+
58
+
59
+ B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
60
+ ===============================================================
61
+
62
+ Python software and documentation are licensed under the
63
+ Python Software Foundation License Version 2.
64
+
65
+ Starting with Python 3.8.6, examples, recipes, and other code in
66
+ the documentation are dual licensed under the PSF License Version 2
67
+ and the Zero-Clause BSD license.
68
+
69
+ Some software incorporated into Python is under different licenses.
70
+ The licenses are listed with code falling under that license.
71
+
72
+
73
+ PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
74
+ --------------------------------------------
75
+
76
+ 1. This LICENSE AGREEMENT is between the Python Software Foundation
77
+ ("PSF"), and the Individual or Organization ("Licensee") accessing and
78
+ otherwise using this software ("Python") in source or binary form and
79
+ its associated documentation.
80
+
81
+ 2. Subject to the terms and conditions of this License Agreement, PSF hereby
82
+ grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
83
+ analyze, test, perform and/or display publicly, prepare derivative works,
84
+ distribute, and otherwise use Python alone or in any derivative version,
85
+ provided, however, that PSF's License Agreement and PSF's notice of copyright,
86
+ i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
87
+ 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation;
88
+ All Rights Reserved" are retained in Python alone or in any derivative version
89
+ prepared by Licensee.
90
+
91
+ 3. In the event Licensee prepares a derivative work that is based on
92
+ or incorporates Python or any part thereof, and wants to make
93
+ the derivative work available to others as provided herein, then
94
+ Licensee hereby agrees to include in any such work a brief summary of
95
+ the changes made to Python.
96
+
97
+ 4. PSF is making Python available to Licensee on an "AS IS"
98
+ basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
99
+ IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
100
+ DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
101
+ FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
102
+ INFRINGE ANY THIRD PARTY RIGHTS.
103
+
104
+ 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
105
+ FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
106
+ A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
107
+ OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
108
+
109
+ 6. This License Agreement will automatically terminate upon a material
110
+ breach of its terms and conditions.
111
+
112
+ 7. Nothing in this License Agreement shall be deemed to create any
113
+ relationship of agency, partnership, or joint venture between PSF and
114
+ Licensee. This License Agreement does not grant permission to use PSF
115
+ trademarks or trade name in a trademark sense to endorse or promote
116
+ products or services of Licensee, or any third party.
117
+
118
+ 8. By copying, installing or otherwise using Python, Licensee
119
+ agrees to be bound by the terms and conditions of this License
120
+ Agreement.
121
+
122
+
123
+ BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
124
+ -------------------------------------------
125
+
126
+ BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
127
+
128
+ 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
129
+ office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
130
+ Individual or Organization ("Licensee") accessing and otherwise using
131
+ this software in source or binary form and its associated
132
+ documentation ("the Software").
133
+
134
+ 2. Subject to the terms and conditions of this BeOpen Python License
135
+ Agreement, BeOpen hereby grants Licensee a non-exclusive,
136
+ royalty-free, world-wide license to reproduce, analyze, test, perform
137
+ and/or display publicly, prepare derivative works, distribute, and
138
+ otherwise use the Software alone or in any derivative version,
139
+ provided, however, that the BeOpen Python License is retained in the
140
+ Software, alone or in any derivative version prepared by Licensee.
141
+
142
+ 3. BeOpen is making the Software available to Licensee on an "AS IS"
143
+ basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
144
+ IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
145
+ DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
146
+ FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
147
+ INFRINGE ANY THIRD PARTY RIGHTS.
148
+
149
+ 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
150
+ SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
151
+ AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
152
+ DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
153
+
154
+ 5. This License Agreement will automatically terminate upon a material
155
+ breach of its terms and conditions.
156
+
157
+ 6. This License Agreement shall be governed by and interpreted in all
158
+ respects by the law of the State of California, excluding conflict of
159
+ law provisions. Nothing in this License Agreement shall be deemed to
160
+ create any relationship of agency, partnership, or joint venture
161
+ between BeOpen and Licensee. This License Agreement does not grant
162
+ permission to use BeOpen trademarks or trade names in a trademark
163
+ sense to endorse or promote products or services of Licensee, or any
164
+ third party. As an exception, the "BeOpen Python" logos available at
165
+ http://www.pythonlabs.com/logos.html may be used according to the
166
+ permissions granted on that web page.
167
+
168
+ 7. By copying, installing or otherwise using the software, Licensee
169
+ agrees to be bound by the terms and conditions of this License
170
+ Agreement.
171
+
172
+
173
+ CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
174
+ ---------------------------------------
175
+
176
+ 1. This LICENSE AGREEMENT is between the Corporation for National
177
+ Research Initiatives, having an office at 1895 Preston White Drive,
178
+ Reston, VA 20191 ("CNRI"), and the Individual or Organization
179
+ ("Licensee") accessing and otherwise using Python 1.6.1 software in
180
+ source or binary form and its associated documentation.
181
+
182
+ 2. Subject to the terms and conditions of this License Agreement, CNRI
183
+ hereby grants Licensee a nonexclusive, royalty-free, world-wide
184
+ license to reproduce, analyze, test, perform and/or display publicly,
185
+ prepare derivative works, distribute, and otherwise use Python 1.6.1
186
+ alone or in any derivative version, provided, however, that CNRI's
187
+ License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
188
+ 1995-2001 Corporation for National Research Initiatives; All Rights
189
+ Reserved" are retained in Python 1.6.1 alone or in any derivative
190
+ version prepared by Licensee. Alternately, in lieu of CNRI's License
191
+ Agreement, Licensee may substitute the following text (omitting the
192
+ quotes): "Python 1.6.1 is made available subject to the terms and
193
+ conditions in CNRI's License Agreement. This Agreement together with
194
+ Python 1.6.1 may be located on the internet using the following
195
+ unique, persistent identifier (known as a handle): 1895.22/1013. This
196
+ Agreement may also be obtained from a proxy server on the internet
197
+ using the following URL: http://hdl.handle.net/1895.22/1013".
198
+
199
+ 3. In the event Licensee prepares a derivative work that is based on
200
+ or incorporates Python 1.6.1 or any part thereof, and wants to make
201
+ the derivative work available to others as provided herein, then
202
+ Licensee hereby agrees to include in any such work a brief summary of
203
+ the changes made to Python 1.6.1.
204
+
205
+ 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
206
+ basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
207
+ IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
208
+ DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
209
+ FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
210
+ INFRINGE ANY THIRD PARTY RIGHTS.
211
+
212
+ 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
213
+ 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
214
+ A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
215
+ OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
216
+
217
+ 6. This License Agreement will automatically terminate upon a material
218
+ breach of its terms and conditions.
219
+
220
+ 7. This License Agreement shall be governed by the federal
221
+ intellectual property law of the United States, including without
222
+ limitation the federal copyright law, and, to the extent such
223
+ U.S. federal law does not apply, by the law of the Commonwealth of
224
+ Virginia, excluding Virginia's conflict of law provisions.
225
+ Notwithstanding the foregoing, with regard to derivative works based
226
+ on Python 1.6.1 that incorporate non-separable material that was
227
+ previously distributed under the GNU General Public License (GPL), the
228
+ law of the Commonwealth of Virginia shall govern this License
229
+ Agreement only as to issues arising under or with respect to
230
+ Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
231
+ License Agreement shall be deemed to create any relationship of
232
+ agency, partnership, or joint venture between CNRI and Licensee. This
233
+ License Agreement does not grant permission to use CNRI trademarks or
234
+ trade name in a trademark sense to endorse or promote products or
235
+ services of Licensee, or any third party.
236
+
237
+ 8. By clicking on the "ACCEPT" button where indicated, or by copying,
238
+ installing or otherwise using Python 1.6.1, Licensee agrees to be
239
+ bound by the terms and conditions of this License Agreement.
240
+
241
+ ACCEPT
242
+
243
+
244
+ CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
245
+ --------------------------------------------------
246
+
247
+ Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
248
+ The Netherlands. All rights reserved.
249
+
250
+ Permission to use, copy, modify, and distribute this software and its
251
+ documentation for any purpose and without fee is hereby granted,
252
+ provided that the above copyright notice appear in all copies and that
253
+ both that copyright notice and this permission notice appear in
254
+ supporting documentation, and that the name of Stichting Mathematisch
255
+ Centrum or CWI not be used in advertising or publicity pertaining to
256
+ distribution of the software without specific, written prior
257
+ permission.
258
+
259
+ STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
260
+ THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
261
+ FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
262
+ FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
263
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
264
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
265
+ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
266
+
267
+ ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
268
+ ----------------------------------------------------------------------
269
+
270
+ Permission to use, copy, modify, and/or distribute this software for any
271
+ purpose with or without fee is hereby granted.
272
+
273
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
274
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
275
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
276
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
277
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
278
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
279
+ PERFORMANCE OF THIS SOFTWARE.
lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.3
2
+ Name: aiohappyeyeballs
3
+ Version: 2.6.1
4
+ Summary: Happy Eyeballs for asyncio
5
+ License: PSF-2.0
6
+ Author: J. Nick Koston
7
+ Author-email: nick@koston.org
8
+ Requires-Python: >=3.9
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Natural Language :: English
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Software Development :: Libraries
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: License :: OSI Approved :: Python Software Foundation License
21
+ Project-URL: Bug Tracker, https://github.com/aio-libs/aiohappyeyeballs/issues
22
+ Project-URL: Changelog, https://github.com/aio-libs/aiohappyeyeballs/blob/main/CHANGELOG.md
23
+ Project-URL: Documentation, https://aiohappyeyeballs.readthedocs.io
24
+ Project-URL: Repository, https://github.com/aio-libs/aiohappyeyeballs
25
+ Description-Content-Type: text/markdown
26
+
27
+ # aiohappyeyeballs
28
+
29
+ <p align="center">
30
+ <a href="https://github.com/aio-libs/aiohappyeyeballs/actions/workflows/ci.yml?query=branch%3Amain">
31
+ <img src="https://img.shields.io/github/actions/workflow/status/aio-libs/aiohappyeyeballs/ci-cd.yml?branch=main&label=CI&logo=github&style=flat-square" alt="CI Status" >
32
+ </a>
33
+ <a href="https://aiohappyeyeballs.readthedocs.io">
34
+ <img src="https://img.shields.io/readthedocs/aiohappyeyeballs.svg?logo=read-the-docs&logoColor=fff&style=flat-square" alt="Documentation Status">
35
+ </a>
36
+ <a href="https://codecov.io/gh/aio-libs/aiohappyeyeballs">
37
+ <img src="https://img.shields.io/codecov/c/github/aio-libs/aiohappyeyeballs.svg?logo=codecov&logoColor=fff&style=flat-square" alt="Test coverage percentage">
38
+ </a>
39
+ </p>
40
+ <p align="center">
41
+ <a href="https://python-poetry.org/">
42
+ <img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">
43
+ </a>
44
+ <a href="https://github.com/astral-sh/ruff">
45
+ <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff">
46
+ </a>
47
+ <a href="https://github.com/pre-commit/pre-commit">
48
+ <img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">
49
+ </a>
50
+ </p>
51
+ <p align="center">
52
+ <a href="https://pypi.org/project/aiohappyeyeballs/">
53
+ <img src="https://img.shields.io/pypi/v/aiohappyeyeballs.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
54
+ </a>
55
+ <img src="https://img.shields.io/pypi/pyversions/aiohappyeyeballs.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">
56
+ <img src="https://img.shields.io/pypi/l/aiohappyeyeballs.svg?style=flat-square" alt="License">
57
+ </p>
58
+
59
+ ---
60
+
61
+ **Documentation**: <a href="https://aiohappyeyeballs.readthedocs.io" target="_blank">https://aiohappyeyeballs.readthedocs.io </a>
62
+
63
+ **Source Code**: <a href="https://github.com/aio-libs/aiohappyeyeballs" target="_blank">https://github.com/aio-libs/aiohappyeyeballs </a>
64
+
65
+ ---
66
+
67
+ [Happy Eyeballs](https://en.wikipedia.org/wiki/Happy_Eyeballs)
68
+ ([RFC 8305](https://www.rfc-editor.org/rfc/rfc8305.html))
69
+
70
+ ## Use case
71
+
72
+ This library exists to allow connecting with
73
+ [Happy Eyeballs](https://en.wikipedia.org/wiki/Happy_Eyeballs)
74
+ ([RFC 8305](https://www.rfc-editor.org/rfc/rfc8305.html))
75
+ when you
76
+ already have a list of addrinfo and not a DNS name.
77
+
78
+ The stdlib version of `loop.create_connection()`
79
+ will only work when you pass in an unresolved name which
80
+ is not a good fit when using DNS caching or resolving
81
+ names via another method such as `zeroconf`.
82
+
83
+ ## Installation
84
+
85
+ Install this via pip (or your favourite package manager):
86
+
87
+ `pip install aiohappyeyeballs`
88
+
89
+ ## License
90
+
91
+ [aiohappyeyeballs is licensed under the same terms as cpython itself.](https://github.com/python/cpython/blob/main/LICENSE)
92
+
93
+ ## Example usage
94
+
95
+ ```python
96
+
97
+ addr_infos = await loop.getaddrinfo("example.org", 80)
98
+
99
+ socket = await start_connection(addr_infos)
100
+ socket = await start_connection(addr_infos, local_addr_infos=local_addr_infos, happy_eyeballs_delay=0.2)
101
+
102
+ transport, protocol = await loop.create_connection(
103
+ MyProtocol, sock=socket, ...)
104
+
105
+ # Remove the first address for each family from addr_info
106
+ pop_addr_infos_interleave(addr_info, 1)
107
+
108
+ # Remove all matching address from addr_info
109
+ remove_addr_infos(addr_info, "dead::beef::")
110
+
111
+ # Convert a local_addr to local_addr_infos
112
+ local_addr_infos = addr_to_addr_infos(("127.0.0.1",0))
113
+ ```
114
+
115
+ ## Credits
116
+
117
+ This package contains code from cpython and is licensed under the same terms as cpython itself.
118
+
119
+ This package was created with
120
+ [Copier](https://copier.readthedocs.io/) and the
121
+ [browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template)
122
+ project template.
123
+
lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/RECORD ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ aiohappyeyeballs-2.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
2
+ aiohappyeyeballs-2.6.1.dist-info/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936
3
+ aiohappyeyeballs-2.6.1.dist-info/METADATA,sha256=NSXlhJwAfi380eEjAo7BQ4P_TVal9xi0qkyZWibMsVM,5915
4
+ aiohappyeyeballs-2.6.1.dist-info/RECORD,,
5
+ aiohappyeyeballs-2.6.1.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
6
+ aiohappyeyeballs/__init__.py,sha256=x7kktHEtaD9quBcWDJPuLeKyjuVAI-Jj14S9B_5hcTs,361
7
+ aiohappyeyeballs/__pycache__/__init__.cpython-312.pyc,,
8
+ aiohappyeyeballs/__pycache__/_staggered.cpython-312.pyc,,
9
+ aiohappyeyeballs/__pycache__/impl.cpython-312.pyc,,
10
+ aiohappyeyeballs/__pycache__/types.cpython-312.pyc,,
11
+ aiohappyeyeballs/__pycache__/utils.cpython-312.pyc,,
12
+ aiohappyeyeballs/_staggered.py,sha256=edfVowFx-P-ywJjIEF3MdPtEMVODujV6CeMYr65otac,6900
13
+ aiohappyeyeballs/impl.py,sha256=Dlcm2mTJ28ucrGnxkb_fo9CZzLAkOOBizOt7dreBbXE,9681
14
+ aiohappyeyeballs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ aiohappyeyeballs/types.py,sha256=YZJIAnyoV4Dz0WFtlaf_OyE4EW7Xus1z7aIfNI6tDDQ,425
16
+ aiohappyeyeballs/utils.py,sha256=on9GxIR0LhEfZu8P6Twi9hepX9zDanuZM20MWsb3xlQ,3028
lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/WHEEL ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.1.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
lib/python3.12/site-packages/fonttools-4.61.1.dist-info/RECORD ADDED
@@ -0,0 +1,689 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ../../../bin/fonttools,sha256=NKIcaBcdPgzS3nnfEpl3yn4gvv1xUv8GdqAn2pnRLaw,253
2
+ ../../../bin/pyftmerge,sha256=WvFMdnNLpg3c-hj-PIDoyRUDiulZoS5-eVzr0WuuN6I,250
3
+ ../../../bin/pyftsubset,sha256=odvcECPVzBH4Qe03IPDPW2V00cI2MpQHJUpbIHeJ3y4,251
4
+ ../../../bin/ttx,sha256=qce32mvSYKqwLyqFH3adWZCHWv5y_2jIw43AhMKJphM,248
5
+ ../../../share/man/man1/ttx.1,sha256=cLbm_pOOj1C76T2QXvDxzwDj9gk-GTd5RztvTMsouFw,5377
6
+ fontTools/__init__.py,sha256=w-yY9D2nPQeBbS3lLIyNrc-4KGqgArD4IhQXdd8qioQ,183
7
+ fontTools/__main__.py,sha256=VjkGh1UD-i1zTDA1dXo1uecSs6PxHdGQ5vlCk_mCCYs,925
8
+ fontTools/__pycache__/__init__.cpython-312.pyc,,
9
+ fontTools/__pycache__/__main__.cpython-312.pyc,,
10
+ fontTools/__pycache__/afmLib.cpython-312.pyc,,
11
+ fontTools/__pycache__/agl.cpython-312.pyc,,
12
+ fontTools/__pycache__/annotations.cpython-312.pyc,,
13
+ fontTools/__pycache__/fontBuilder.cpython-312.pyc,,
14
+ fontTools/__pycache__/help.cpython-312.pyc,,
15
+ fontTools/__pycache__/tfmLib.cpython-312.pyc,,
16
+ fontTools/__pycache__/ttx.cpython-312.pyc,,
17
+ fontTools/__pycache__/unicode.cpython-312.pyc,,
18
+ fontTools/afmLib.py,sha256=1MagIItOzRV4vV5kKPxeDZbPJsfxLB3wdHLFkQvl0uk,13164
19
+ fontTools/agl.py,sha256=05bm8Uq45uVWW8nPbP6xbNgmFyxQr8sWhYAiP0VSjnI,112975
20
+ fontTools/annotations.py,sha256=BdIIriNYDzBfgniwWFg_u71qLZnV0sCcn4-VAkXkYNM,1225
21
+ fontTools/cffLib/CFF2ToCFF.py,sha256=7h0fK53ji4JlA4mAxlVb54eQTM4N2e3LAd4wwW3TLyE,8201
22
+ fontTools/cffLib/CFFToCFF2.py,sha256=Qnk7lYlsTRHnlZQ6NXNdr_f4MJwZQ21kcS08KFbsyY8,10119
23
+ fontTools/cffLib/__init__.py,sha256=62vpcR7u8cE407kXduAwnFttHnsoCpDQ7IBK-qOYFQ8,107886
24
+ fontTools/cffLib/__pycache__/CFF2ToCFF.cpython-312.pyc,,
25
+ fontTools/cffLib/__pycache__/CFFToCFF2.cpython-312.pyc,,
26
+ fontTools/cffLib/__pycache__/__init__.cpython-312.pyc,,
27
+ fontTools/cffLib/__pycache__/specializer.cpython-312.pyc,,
28
+ fontTools/cffLib/__pycache__/transforms.cpython-312.pyc,,
29
+ fontTools/cffLib/__pycache__/width.cpython-312.pyc,,
30
+ fontTools/cffLib/specializer.py,sha256=vsOPkR_jHNe6tESQEjmm0i76y7sWI5MKo3bsTmI3sNM,32609
31
+ fontTools/cffLib/transforms.py,sha256=SEIZc1XxWYiVXVBsoNm6LTvM9SUN7Z76QOaSAlR1ZCo,17455
32
+ fontTools/cffLib/width.py,sha256=IqGL0CLyCZqi_hvsHySG08qpYxS3kaqW-tsAT-bjHV4,6074
33
+ fontTools/colorLib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ fontTools/colorLib/__pycache__/__init__.cpython-312.pyc,,
35
+ fontTools/colorLib/__pycache__/builder.cpython-312.pyc,,
36
+ fontTools/colorLib/__pycache__/errors.cpython-312.pyc,,
37
+ fontTools/colorLib/__pycache__/geometry.cpython-312.pyc,,
38
+ fontTools/colorLib/__pycache__/table_builder.cpython-312.pyc,,
39
+ fontTools/colorLib/__pycache__/unbuilder.cpython-312.pyc,,
40
+ fontTools/colorLib/builder.py,sha256=kmO7OuudQQb3fEOS7aLzgTDVjqS9i2xIQmk9p1uBe8A,23008
41
+ fontTools/colorLib/errors.py,sha256=CsaviiRxxrpgVX4blm7KCyK8553ljwL44xkJOeC5U7U,41
42
+ fontTools/colorLib/geometry.py,sha256=3ScySrR2YDJa7d5K5_xM5Yt1-3NCV-ry8ikYA5VwVbI,5518
43
+ fontTools/colorLib/table_builder.py,sha256=ZeltWY6n-YPiJv_hQ1iBXoEFAG70EKxZyScgsMKUFGU,7469
44
+ fontTools/colorLib/unbuilder.py,sha256=iW-E5I39WsV82K3NgCO4Cjzwm1WqzGrtypHt8epwbHM,2142
45
+ fontTools/config/__init__.py,sha256=JICOHIz06KuHCiBmrxj-ga19P6ZTuLXh0lHmPh-Ra1w,3154
46
+ fontTools/config/__pycache__/__init__.cpython-312.pyc,,
47
+ fontTools/cu2qu/__init__.py,sha256=Cuc7Uglb0nSgaraTxXY5J8bReznH5wApW0uakN7MycY,618
48
+ fontTools/cu2qu/__main__.py,sha256=kTUI-jczsHeelULLlory74QEeFjZWp9zigCc7PrdVQY,92
49
+ fontTools/cu2qu/__pycache__/__init__.cpython-312.pyc,,
50
+ fontTools/cu2qu/__pycache__/__main__.cpython-312.pyc,,
51
+ fontTools/cu2qu/__pycache__/benchmark.cpython-312.pyc,,
52
+ fontTools/cu2qu/__pycache__/cli.cpython-312.pyc,,
53
+ fontTools/cu2qu/__pycache__/cu2qu.cpython-312.pyc,,
54
+ fontTools/cu2qu/__pycache__/errors.cpython-312.pyc,,
55
+ fontTools/cu2qu/__pycache__/ufo.cpython-312.pyc,,
56
+ fontTools/cu2qu/benchmark.py,sha256=wasPJmf8q9k9UHjpHChC3WQAGbBAyHN9PvJzXvWC0Fw,1296
57
+ fontTools/cu2qu/cli.py,sha256=MbAQnOpZwrUFe_tjAP3Tgf6uLdOgHlONUcPNeTXwH0Y,6076
58
+ fontTools/cu2qu/cu2qu.c,sha256=Hacw9XncC5wYjSd-uT4MxoOQGDBQFnG8mLZrohGHtXQ,648759
59
+ fontTools/cu2qu/cu2qu.cpython-312-x86_64-linux-gnu.so,sha256=IqlIidvItzEAjZ5Lko1FBJUsMj-oWloXxop80oBShRw,1082272
60
+ fontTools/cu2qu/cu2qu.py,sha256=6LTe1ZI-jxW8y79s_UFjbkeFoFleIekTLm2jAE-uqGQ,17986
61
+ fontTools/cu2qu/errors.py,sha256=PyJNMy8lHDtKpfFkc0nkM8F4jNLZAC4lPQCN1Km4bpg,2441
62
+ fontTools/cu2qu/ufo.py,sha256=g2NkcMqdS-t3tBtvcqtt4E02QVyQr3pTC1qXDRtI31U,12307
63
+ fontTools/designspaceLib/__init__.py,sha256=whzi46QY0FK77k9iE646rGP4Ivlq9BEXKO0y7UU9Plo,129502
64
+ fontTools/designspaceLib/__main__.py,sha256=xhtYXo1T1tsykhQDD0tcconSNYgWL5hoTBORpVDUYrc,103
65
+ fontTools/designspaceLib/__pycache__/__init__.cpython-312.pyc,,
66
+ fontTools/designspaceLib/__pycache__/__main__.cpython-312.pyc,,
67
+ fontTools/designspaceLib/__pycache__/split.cpython-312.pyc,,
68
+ fontTools/designspaceLib/__pycache__/statNames.cpython-312.pyc,,
69
+ fontTools/designspaceLib/__pycache__/types.cpython-312.pyc,,
70
+ fontTools/designspaceLib/split.py,sha256=FB1NuvhUO453UXveQZi9oyrW_caoCPM3RADp1rYWkDs,19239
71
+ fontTools/designspaceLib/statNames.py,sha256=gXGKWVr1ju2_oL-R_DkyoZ3GlI7mfLORovpk1Ebgmvc,9237
72
+ fontTools/designspaceLib/types.py,sha256=ofK65qXNADqcpl7zI72Pa5s07-cm7G41iEmLVV44-Es,5320
73
+ fontTools/encodings/MacRoman.py,sha256=4vEooUDm2gLCG8KIIDhRxm5-A64w7XrhP9cjDRr2Eo0,3576
74
+ fontTools/encodings/StandardEncoding.py,sha256=Eo3AGE8FE_p-IVYYuV097KouSsF3UrXoRRN0XyvYbrs,3581
75
+ fontTools/encodings/__init__.py,sha256=DJBWmoX_Haau7qlgmvWyfbhSzrX2qL636Rns7CG01pk,75
76
+ fontTools/encodings/__pycache__/MacRoman.cpython-312.pyc,,
77
+ fontTools/encodings/__pycache__/StandardEncoding.cpython-312.pyc,,
78
+ fontTools/encodings/__pycache__/__init__.cpython-312.pyc,,
79
+ fontTools/encodings/__pycache__/codecs.cpython-312.pyc,,
80
+ fontTools/encodings/codecs.py,sha256=u50ruwz9fcRsrUrRGpR17Cr55Ovn1fvCHCKrElVumDE,4721
81
+ fontTools/feaLib/__init__.py,sha256=jlIru2ghxvb1HhC5Je2BCXjFJmFQlYKpruorPoz3BvQ,213
82
+ fontTools/feaLib/__main__.py,sha256=Df2PA6LXwna98lSXiL7R4as_ZEdWCIk3egSM5w7GpvM,2240
83
+ fontTools/feaLib/__pycache__/__init__.cpython-312.pyc,,
84
+ fontTools/feaLib/__pycache__/__main__.cpython-312.pyc,,
85
+ fontTools/feaLib/__pycache__/ast.cpython-312.pyc,,
86
+ fontTools/feaLib/__pycache__/builder.cpython-312.pyc,,
87
+ fontTools/feaLib/__pycache__/error.cpython-312.pyc,,
88
+ fontTools/feaLib/__pycache__/lexer.cpython-312.pyc,,
89
+ fontTools/feaLib/__pycache__/location.cpython-312.pyc,,
90
+ fontTools/feaLib/__pycache__/lookupDebugInfo.cpython-312.pyc,,
91
+ fontTools/feaLib/__pycache__/parser.cpython-312.pyc,,
92
+ fontTools/feaLib/__pycache__/variableScalar.cpython-312.pyc,,
93
+ fontTools/feaLib/ast.py,sha256=48Y6vpSD_wYfucWyh_bQtzf2AQFX-pOwBvsxdcpVDz0,74158
94
+ fontTools/feaLib/builder.py,sha256=OI3slR3tTlH1LyWGHJMDw3jol5PzvRd8_T5j1603xZM,73968
95
+ fontTools/feaLib/error.py,sha256=Bz_5tNcNVcY7_nrAmFlQNhQldtqZWd8WUGQ2E3PWhZo,648
96
+ fontTools/feaLib/lexer.c,sha256=LWJfBpHqWqXW1IAGjiFT7pcXWmJWX55FjrQPvN3d9uo,747744
97
+ fontTools/feaLib/lexer.cpython-312-x86_64-linux-gnu.so,sha256=C1G2mAF0TH-ZfPsd5PYxiTQTeuEcJpSy-0m34L75XEE,1388240
98
+ fontTools/feaLib/lexer.py,sha256=emyMPmRoqNZkzxnJyI6JRCCtXrbCOFofwa9O6ABGLiw,11121
99
+ fontTools/feaLib/location.py,sha256=JXzHqGV56EHdcq823AwA5oaK05hf_1ySWpScbo3zGC0,234
100
+ fontTools/feaLib/lookupDebugInfo.py,sha256=gVRr5-APWfT_a5-25hRuawSVX8fEvXVsOSLWkH91T2w,304
101
+ fontTools/feaLib/parser.py,sha256=eM4ph-_6ab8EpAWqg9Ax7is5We-KwkzJYlui31mJSBc,99911
102
+ fontTools/feaLib/variableScalar.py,sha256=f6sOg9cfFJRI3fw04uRohDeFux0xnZanaPT_lcxAVOw,4200
103
+ fontTools/fontBuilder.py,sha256=d9bBaId9K6KPpR5nP0l5Ou_8Pp7hr4gRuLeQV0NmwUY,34280
104
+ fontTools/help.py,sha256=bAjatvIhV7TJyXI7WhsxdYO4YVlhScZXu_kRtHANEPo,1125
105
+ fontTools/merge/__init__.py,sha256=8i6ownyQTAOBKWnTEHvvCYFw64Mv7Z1HPBgJI-ZiuKo,8256
106
+ fontTools/merge/__main__.py,sha256=hDx3gfbUBO83AJKumSEhiV-xqNTJNNgK2uFjazOGTmw,94
107
+ fontTools/merge/__pycache__/__init__.cpython-312.pyc,,
108
+ fontTools/merge/__pycache__/__main__.cpython-312.pyc,,
109
+ fontTools/merge/__pycache__/base.cpython-312.pyc,,
110
+ fontTools/merge/__pycache__/cmap.cpython-312.pyc,,
111
+ fontTools/merge/__pycache__/layout.cpython-312.pyc,,
112
+ fontTools/merge/__pycache__/options.cpython-312.pyc,,
113
+ fontTools/merge/__pycache__/tables.cpython-312.pyc,,
114
+ fontTools/merge/__pycache__/unicode.cpython-312.pyc,,
115
+ fontTools/merge/__pycache__/util.cpython-312.pyc,,
116
+ fontTools/merge/base.py,sha256=l0G1Px98E9ZdVuFLMUBKWdtr7Jb8JX8vxcjeaDUUnzY,2389
117
+ fontTools/merge/cmap.py,sha256=HpthxVH5lA7VegJ8yHoBjd9vrFBV7UB5OknKGYpxWY8,6728
118
+ fontTools/merge/layout.py,sha256=fkMPGPLxEdxohS3scVM4W7LmNthSz-UPyocsffe2KqE,16075
119
+ fontTools/merge/options.py,sha256=xko_1-WErcNQkirECzIOOYxSJR_bRtdQYQYOtmgccYI,2501
120
+ fontTools/merge/tables.py,sha256=7SzXYL04awDEDhvU2-9T_8A2gAjvgGyYAHUICUJOpZg,10958
121
+ fontTools/merge/unicode.py,sha256=kb1Jrfuoq1KUcVhhSKnflAED_wMZxXDjVwB-CI9k05Y,4273
122
+ fontTools/merge/util.py,sha256=BH3bZWNFy-Tsj1cth7aSpGVJ18YXKXqDakPn6Wzku6U,3378
123
+ fontTools/misc/__init__.py,sha256=DJBWmoX_Haau7qlgmvWyfbhSzrX2qL636Rns7CG01pk,75
124
+ fontTools/misc/__pycache__/__init__.cpython-312.pyc,,
125
+ fontTools/misc/__pycache__/arrayTools.cpython-312.pyc,,
126
+ fontTools/misc/__pycache__/bezierTools.cpython-312.pyc,,
127
+ fontTools/misc/__pycache__/classifyTools.cpython-312.pyc,,
128
+ fontTools/misc/__pycache__/cliTools.cpython-312.pyc,,
129
+ fontTools/misc/__pycache__/configTools.cpython-312.pyc,,
130
+ fontTools/misc/__pycache__/cython.cpython-312.pyc,,
131
+ fontTools/misc/__pycache__/dictTools.cpython-312.pyc,,
132
+ fontTools/misc/__pycache__/eexec.cpython-312.pyc,,
133
+ fontTools/misc/__pycache__/encodingTools.cpython-312.pyc,,
134
+ fontTools/misc/__pycache__/enumTools.cpython-312.pyc,,
135
+ fontTools/misc/__pycache__/etree.cpython-312.pyc,,
136
+ fontTools/misc/__pycache__/filenames.cpython-312.pyc,,
137
+ fontTools/misc/__pycache__/fixedTools.cpython-312.pyc,,
138
+ fontTools/misc/__pycache__/intTools.cpython-312.pyc,,
139
+ fontTools/misc/__pycache__/iterTools.cpython-312.pyc,,
140
+ fontTools/misc/__pycache__/lazyTools.cpython-312.pyc,,
141
+ fontTools/misc/__pycache__/loggingTools.cpython-312.pyc,,
142
+ fontTools/misc/__pycache__/macCreatorType.cpython-312.pyc,,
143
+ fontTools/misc/__pycache__/macRes.cpython-312.pyc,,
144
+ fontTools/misc/__pycache__/psCharStrings.cpython-312.pyc,,
145
+ fontTools/misc/__pycache__/psLib.cpython-312.pyc,,
146
+ fontTools/misc/__pycache__/psOperators.cpython-312.pyc,,
147
+ fontTools/misc/__pycache__/py23.cpython-312.pyc,,
148
+ fontTools/misc/__pycache__/roundTools.cpython-312.pyc,,
149
+ fontTools/misc/__pycache__/sstruct.cpython-312.pyc,,
150
+ fontTools/misc/__pycache__/symfont.cpython-312.pyc,,
151
+ fontTools/misc/__pycache__/testTools.cpython-312.pyc,,
152
+ fontTools/misc/__pycache__/textTools.cpython-312.pyc,,
153
+ fontTools/misc/__pycache__/timeTools.cpython-312.pyc,,
154
+ fontTools/misc/__pycache__/transform.cpython-312.pyc,,
155
+ fontTools/misc/__pycache__/treeTools.cpython-312.pyc,,
156
+ fontTools/misc/__pycache__/vector.cpython-312.pyc,,
157
+ fontTools/misc/__pycache__/visitor.cpython-312.pyc,,
158
+ fontTools/misc/__pycache__/xmlReader.cpython-312.pyc,,
159
+ fontTools/misc/__pycache__/xmlWriter.cpython-312.pyc,,
160
+ fontTools/misc/arrayTools.py,sha256=jZk__GE-K9VViZE_H-LPPj0smWbKng-yfPE8BfGp8HI,11483
161
+ fontTools/misc/bezierTools.c,sha256=7ymo3Ld3xnDe1fJBXiqMVMFzoYRc5hY9Qxby9g3r5XI,1836341
162
+ fontTools/misc/bezierTools.cpython-312-x86_64-linux-gnu.so,sha256=TiZqyjMhcxmhLrG4DzMkKDcx0sR_6dyo7uqXbq6C9NA,4786264
163
+ fontTools/misc/bezierTools.py,sha256=2xvnsKboUZ-Ln1bbiJk2392foh3vRhulmpqaWl1a-lQ,45240
164
+ fontTools/misc/classifyTools.py,sha256=zcg3EM4GOerBW9c063ljaLllgeeZ772EpFZjp9CdgLI,5613
165
+ fontTools/misc/cliTools.py,sha256=qCznJMLCQu3ZHQD_4ctUnr3TkfAUdkGl-UuxZUrppy0,1862
166
+ fontTools/misc/configTools.py,sha256=tDede_q8h81w6mDh132_F5WzfWkwmDGAF8nwj3dHVus,11229
167
+ fontTools/misc/cython.py,sha256=eyLcL2Bw-SSToYro8f44dkkYRlQfiFbhcza0afS-qHE,682
168
+ fontTools/misc/dictTools.py,sha256=VxjarsGJuk_wa3z29FSCtKZNCFfXtMBiNEu0RPAlpDk,2417
169
+ fontTools/misc/eexec.py,sha256=GNn2OCRvO1HbbIeDPxk9i0glO7cux_AQaoVMXhBR8y8,3331
170
+ fontTools/misc/encodingTools.py,sha256=hCv5PFfnXQJVCZA8Wyn1vr3vzLBbUuEPtGk5CzWM9RY,2073
171
+ fontTools/misc/enumTools.py,sha256=YQZW-d2ES9KFFkAXOUMIBbRUk6v_3BT6Q7lXE1ufhxA,502
172
+ fontTools/misc/etree.py,sha256=ZzJc6TvAS579deAgZLVDvTY_HeTm-ZsKJ5s3LYhZSSY,16304
173
+ fontTools/misc/filenames.py,sha256=MMCO3xjk1pcDc-baobcKd8IdoFPt-bcGqu8t8HUGAkI,8223
174
+ fontTools/misc/filesystem/__init__.py,sha256=iwoOj6DpXKk8q-NRRHqOfRxFF6lcXIhsIA46j-cZswU,2011
175
+ fontTools/misc/filesystem/__pycache__/__init__.cpython-312.pyc,,
176
+ fontTools/misc/filesystem/__pycache__/_base.cpython-312.pyc,,
177
+ fontTools/misc/filesystem/__pycache__/_copy.cpython-312.pyc,,
178
+ fontTools/misc/filesystem/__pycache__/_errors.cpython-312.pyc,,
179
+ fontTools/misc/filesystem/__pycache__/_info.cpython-312.pyc,,
180
+ fontTools/misc/filesystem/__pycache__/_osfs.cpython-312.pyc,,
181
+ fontTools/misc/filesystem/__pycache__/_path.cpython-312.pyc,,
182
+ fontTools/misc/filesystem/__pycache__/_subfs.cpython-312.pyc,,
183
+ fontTools/misc/filesystem/__pycache__/_tempfs.cpython-312.pyc,,
184
+ fontTools/misc/filesystem/__pycache__/_tools.cpython-312.pyc,,
185
+ fontTools/misc/filesystem/__pycache__/_walk.cpython-312.pyc,,
186
+ fontTools/misc/filesystem/__pycache__/_zipfs.cpython-312.pyc,,
187
+ fontTools/misc/filesystem/_base.py,sha256=p74O7xREadfPQgzPJ9mP3ehu0ZHDgsmXlpsL9CnTRso,4010
188
+ fontTools/misc/filesystem/_copy.py,sha256=ifMSs-A_bz1Aa4tIQrlUd9HtdJQ5fp5M3B6mbYuDtXI,1361
189
+ fontTools/misc/filesystem/_errors.py,sha256=-YziRB1BT1I80ypmufvCR-M_4XoerCHyBVqX-cRnIzU,641
190
+ fontTools/misc/filesystem/_info.py,sha256=pbV7bDTJ5F8ms6alK34J0FZYWzmRO7FT0NM3yRA3czo,2013
191
+ fontTools/misc/filesystem/_osfs.py,sha256=RkKCE2IxcRaj7gyqFW10LhEm_-VJYtXxsS5s0DCXihM,5737
192
+ fontTools/misc/filesystem/_path.py,sha256=frP6ZLmMeP9E3NiwoCbbgBPWpQbLBRh7T-0vOE-EuPo,1745
193
+ fontTools/misc/filesystem/_subfs.py,sha256=vRotQwyLVfINbR88xIBQUbq9j4Kmg1_mJvEhnpvK_t4,3028
194
+ fontTools/misc/filesystem/_tempfs.py,sha256=9FUXdCBTwFtZMFx8ghYuZVYoQdDb0tDB-jXNu3D-Qy0,924
195
+ fontTools/misc/filesystem/_tools.py,sha256=r75dpadp7C9EdQ6r7pJQKZlCZDUJzVq2ikb_LXN-wCI,972
196
+ fontTools/misc/filesystem/_walk.py,sha256=KMQ-GavWYr4SsA5V8ohLPmz3boilvY2P0JKrLoxW6NU,1655
197
+ fontTools/misc/filesystem/_zipfs.py,sha256=i3qolbkDRntB_oL3v79KuEgfVlVojecPBnBA0X04PWc,6301
198
+ fontTools/misc/fixedTools.py,sha256=XycD5QpaejJE-GFaFMlLlku6RG0ipJowuamMXjFrMjQ,7668
199
+ fontTools/misc/intTools.py,sha256=l6pjk4UYlXcyLtfC0DdOC5RL6UJ8ihRR0zRiYow5xA8,586
200
+ fontTools/misc/iterTools.py,sha256=17H6LPZszp32bTKoNorp6uZF1PKj47BAbe5QG8irUjo,390
201
+ fontTools/misc/lazyTools.py,sha256=BC6MmF-OzJ3GrBD8TYDZ-VCSN4UOx0pN0r3oF4GSoiw,1020
202
+ fontTools/misc/loggingTools.py,sha256=NOYROsLK5TzONK5967OGdVonNyXC6kP_CmPr7M2PW_c,19933
203
+ fontTools/misc/macCreatorType.py,sha256=Je9jtqUr7EPbpH3QxlVl3pizoQ-1AOPMBIctHIMTM3k,1593
204
+ fontTools/misc/macRes.py,sha256=GT_pnfPw2NCvvOF86nHLAnOtZ6SMHqEuLntaplXzvHM,8579
205
+ fontTools/misc/plistlib/__init__.py,sha256=1HfhHPt3As6u2eRSlFfl6XdnXv_ypQImeQdWIw6wK7Y,21113
206
+ fontTools/misc/plistlib/__pycache__/__init__.cpython-312.pyc,,
207
+ fontTools/misc/plistlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
208
+ fontTools/misc/psCharStrings.py,sha256=Zn8mr7NRTfEJbnaXbOnSarrFBPcvPM0AupAxF2C80LY,43468
209
+ fontTools/misc/psLib.py,sha256=ioIPm5x3MHkBXF2vzNkC4iVZYobrkWcyvFhmYsjOrPY,12099
210
+ fontTools/misc/psOperators.py,sha256=9SLl5PPBulLo0Xxg_dqlJMitNIBdiGKdkXhOWsNSYZE,15700
211
+ fontTools/misc/py23.py,sha256=aPVCEUz_deggwLBCeTSsccX6QgJavZqvdVtuhpzrPvA,2238
212
+ fontTools/misc/roundTools.py,sha256=1RSXZ0gyi1qW42tz6WSBMJD1FlPdtgqKfWixVN9bd78,3173
213
+ fontTools/misc/sstruct.py,sha256=vUODd2CKvHLtjr7yn1K94Hui_yxOPWKmlgAmBMm3KDQ,7009
214
+ fontTools/misc/symfont.py,sha256=x5ZwqK9Ik9orG6qSftgVGygBFE1wTSngrMK2We1Z5AM,6977
215
+ fontTools/misc/testTools.py,sha256=3vj_KllUQVEiVFbS0SzTmeuKv44-L-disI1dZ4XhOfw,7052
216
+ fontTools/misc/textTools.py,sha256=wNjH5zl1v9qNfmTl4BL52IO2IG1H5xY3o_pslqPPRjc,3483
217
+ fontTools/misc/timeTools.py,sha256=e9h5pgzL04tBDXmCv_8eRGB4boFV8GKXlS6dq3ggEpw,2234
218
+ fontTools/misc/transform.py,sha256=OR8dPsAw87z77gkZQMq00iUkDWLIxYv-12XiKH1-erk,15798
219
+ fontTools/misc/treeTools.py,sha256=tLWkwyDHeZUPVOGNnJeD4Pn7x2bQeZetwJKaEAW2J2M,1269
220
+ fontTools/misc/vector.py,sha256=6lqZcDjAgHJFQgjzD-ULQ_PrigAMfeZKaBZmAfcC0ig,4062
221
+ fontTools/misc/visitor.py,sha256=zwBAVfZ3MTsrbhNFj03pSSjNRyT6oGkare-kfWkN5ns,5754
222
+ fontTools/misc/xmlReader.py,sha256=igut4_d13RT4WarliqVvuuPybO1uSXVeoBOeW4j0_e4,6580
223
+ fontTools/misc/xmlWriter.py,sha256=EIdcS_0y9gvpc82QvZC9VcerAqEsP8eeC0gqMoPK4jc,7374
224
+ fontTools/mtiLib/__init__.py,sha256=EzYwNaENLf906h1THBeq6nSRHUKpOAYxuzO9x9PHzh8,46602
225
+ fontTools/mtiLib/__main__.py,sha256=gd8X89jnZOe-752k7uaR1lWoiju-2zIT5Yx35Kl0Xek,94
226
+ fontTools/mtiLib/__pycache__/__init__.cpython-312.pyc,,
227
+ fontTools/mtiLib/__pycache__/__main__.cpython-312.pyc,,
228
+ fontTools/otlLib/__init__.py,sha256=D2leUW-3gsUTOFcJYGC18edBYjIJ804ut4qitJYWsaQ,45
229
+ fontTools/otlLib/__pycache__/__init__.cpython-312.pyc,,
230
+ fontTools/otlLib/__pycache__/builder.cpython-312.pyc,,
231
+ fontTools/otlLib/__pycache__/error.cpython-312.pyc,,
232
+ fontTools/otlLib/__pycache__/maxContextCalc.cpython-312.pyc,,
233
+ fontTools/otlLib/builder.py,sha256=DEhmbyVzsPFwCDrxgIjfOaZSIcKcVmCwrg9KMxkksEs,129088
234
+ fontTools/otlLib/error.py,sha256=cthuhBuOwZYpkTLi5gFPupUxkXkCHe-L_YgkE7N1wCI,335
235
+ fontTools/otlLib/maxContextCalc.py,sha256=3es4Kt84TaZ49sA2ev1zrlwPJikJCAECx5KavwhyB-I,3175
236
+ fontTools/otlLib/optimize/__init__.py,sha256=UUQRpNkHU2RczCRt-Gz7sEiYE9AQq9BHLXZEOyvsnX4,1530
237
+ fontTools/otlLib/optimize/__main__.py,sha256=BvP472kA9KxBb9RMyyehPNevAfpmgW9MfdazkUiAO3M,104
238
+ fontTools/otlLib/optimize/__pycache__/__init__.cpython-312.pyc,,
239
+ fontTools/otlLib/optimize/__pycache__/__main__.cpython-312.pyc,,
240
+ fontTools/otlLib/optimize/__pycache__/gpos.cpython-312.pyc,,
241
+ fontTools/otlLib/optimize/gpos.py,sha256=htOgSP743DZDUKF3eWAeJ-kdqNYOnpGXdlV-rbEXJ1A,17668
242
+ fontTools/pens/__init__.py,sha256=DJBWmoX_Haau7qlgmvWyfbhSzrX2qL636Rns7CG01pk,75
243
+ fontTools/pens/__pycache__/__init__.cpython-312.pyc,,
244
+ fontTools/pens/__pycache__/areaPen.cpython-312.pyc,,
245
+ fontTools/pens/__pycache__/basePen.cpython-312.pyc,,
246
+ fontTools/pens/__pycache__/boundsPen.cpython-312.pyc,,
247
+ fontTools/pens/__pycache__/cairoPen.cpython-312.pyc,,
248
+ fontTools/pens/__pycache__/cocoaPen.cpython-312.pyc,,
249
+ fontTools/pens/__pycache__/cu2quPen.cpython-312.pyc,,
250
+ fontTools/pens/__pycache__/explicitClosingLinePen.cpython-312.pyc,,
251
+ fontTools/pens/__pycache__/filterPen.cpython-312.pyc,,
252
+ fontTools/pens/__pycache__/freetypePen.cpython-312.pyc,,
253
+ fontTools/pens/__pycache__/hashPointPen.cpython-312.pyc,,
254
+ fontTools/pens/__pycache__/momentsPen.cpython-312.pyc,,
255
+ fontTools/pens/__pycache__/perimeterPen.cpython-312.pyc,,
256
+ fontTools/pens/__pycache__/pointInsidePen.cpython-312.pyc,,
257
+ fontTools/pens/__pycache__/pointPen.cpython-312.pyc,,
258
+ fontTools/pens/__pycache__/qtPen.cpython-312.pyc,,
259
+ fontTools/pens/__pycache__/qu2cuPen.cpython-312.pyc,,
260
+ fontTools/pens/__pycache__/quartzPen.cpython-312.pyc,,
261
+ fontTools/pens/__pycache__/recordingPen.cpython-312.pyc,,
262
+ fontTools/pens/__pycache__/reportLabPen.cpython-312.pyc,,
263
+ fontTools/pens/__pycache__/reverseContourPen.cpython-312.pyc,,
264
+ fontTools/pens/__pycache__/roundingPen.cpython-312.pyc,,
265
+ fontTools/pens/__pycache__/statisticsPen.cpython-312.pyc,,
266
+ fontTools/pens/__pycache__/svgPathPen.cpython-312.pyc,,
267
+ fontTools/pens/__pycache__/t2CharStringPen.cpython-312.pyc,,
268
+ fontTools/pens/__pycache__/teePen.cpython-312.pyc,,
269
+ fontTools/pens/__pycache__/transformPen.cpython-312.pyc,,
270
+ fontTools/pens/__pycache__/ttGlyphPen.cpython-312.pyc,,
271
+ fontTools/pens/__pycache__/wxPen.cpython-312.pyc,,
272
+ fontTools/pens/areaPen.py,sha256=Y1WkmqzcC4z_bpGAR0IZUKrtHFtxKUQBmr5-64_zCOk,1472
273
+ fontTools/pens/basePen.py,sha256=eIGSKrKm6w4LLHuG6XJoQZ3eObtoKV5P6aF4gT4sk7U,17073
274
+ fontTools/pens/boundsPen.py,sha256=wE3owOQA8DfhH-zBGC3lJvnVwp-oyIt0KZrEqXbmS9I,3129
275
+ fontTools/pens/cairoPen.py,sha256=wuuOJ1qQDSt_K3zscM2nukRyHZTZMwMzzCXCirfq_qQ,592
276
+ fontTools/pens/cocoaPen.py,sha256=IJRQcAxRuVOTQ90bB_Bgjnmz7px_ST5uLF9CW-Y0KPY,612
277
+ fontTools/pens/cu2quPen.py,sha256=gMUwFUsm_-WzBlDjTMQiNnEuI2heomGeOJBX81zYXPo,13007
278
+ fontTools/pens/explicitClosingLinePen.py,sha256=kKKtdZiwaf8Cj4_ytrIDdGB2GMpPPDXm5Nwbw5WDgwU,3219
279
+ fontTools/pens/filterPen.py,sha256=REgspXaaSvF3XUwqe40abe3X_E7-WbBP13IqLUUBLCw,14703
280
+ fontTools/pens/freetypePen.py,sha256=HD-gXJSbgImJdBc8sIBk0HWBdjv3WKFofs6PgCCsGOY,19908
281
+ fontTools/pens/hashPointPen.py,sha256=gElrFyQoOQp3ZbpKHRWPwC61A9OgT2Js8crVUD8BQAY,3573
282
+ fontTools/pens/momentsPen.c,sha256=YVgVJ8eTQIscGOc4zNztWjIdC4RGkiqLLpm8dDBamPY,567692
283
+ fontTools/pens/momentsPen.cpython-312-x86_64-linux-gnu.so,sha256=_E9DVON4gfy3F8IDWnpbbpvJXk62xPyFfD6xmvxLjQM,986720
284
+ fontTools/pens/momentsPen.py,sha256=kjLVXhGe55Abl__Yr1gob0bl0dHe7fPSwyr7TRJnbug,25658
285
+ fontTools/pens/perimeterPen.py,sha256=lr6NzrIWxi4TXBJPbcJsKzqABWfQeil2Bgm9BgUD3N4,2153
286
+ fontTools/pens/pointInsidePen.py,sha256=noEUvBQIeAheDMJwzvvfnEiKhmwbS1i0RQE9jik6Gl4,6355
287
+ fontTools/pens/pointPen.py,sha256=oeE_uabVCNJ1Lpk5Hn3eBmafaao3QqKMjK6FAy0hKBo,24197
288
+ fontTools/pens/qtPen.py,sha256=QRNLIry2rQl4E_7ct2tu10-qLHneQp0XV7FfaZ-tcL8,634
289
+ fontTools/pens/qu2cuPen.py,sha256=pRST43-rUpzlOP83Z_Rr0IvIQBCx6RWI6nnNaitQcLk,3985
290
+ fontTools/pens/quartzPen.py,sha256=EH482Kz_xsqYhVRovv6N_T1CXaSvOzUKPLxTaN956tU,1287
291
+ fontTools/pens/recordingPen.py,sha256=VgFZ4NMhnZt1qSTzFEU0cma-gw3kBe47bfSxPYH73rs,12489
292
+ fontTools/pens/reportLabPen.py,sha256=kpfMfOLXt2vOQ5smPsU82ft80FpCPWJzQLl7ENOH8Ew,2066
293
+ fontTools/pens/reverseContourPen.py,sha256=oz64ZRhLAvT7DYMAwGKoLzZXQK8l81jRiYnTZkW6a-Y,4022
294
+ fontTools/pens/roundingPen.py,sha256=vh_FjikRd82-S4I8glgGMGEuGrj5IkCjRT_wmZ8jfqY,4620
295
+ fontTools/pens/statisticsPen.py,sha256=piWK6NjjWqk9MLROjeE2-4EsxVYMyNU7UQFGD_trE9g,9808
296
+ fontTools/pens/svgPathPen.py,sha256=T3b6SZS9B9sVWMK9mSFDtjHeviQs_yOJOZKq5Sg5Zdg,8572
297
+ fontTools/pens/t2CharStringPen.py,sha256=GgGklb5XsCer0w37ujgRLRXx-EuzdFsyCYuzCx4n-Qs,2931
298
+ fontTools/pens/teePen.py,sha256=P1ARJOCMJ6MxK-PB1yZ-ips3CUfnadWYnQ_do6VIasQ,1290
299
+ fontTools/pens/transformPen.py,sha256=s0kUyQdnemUwHvYr2SFboFmh4WY1S9OHBL8L4PJKRwE,4056
300
+ fontTools/pens/ttGlyphPen.py,sha256=yLtB-E5pTQR59OKVYySttWBu1xC2vR8ezSaRhIMtVwg,11870
301
+ fontTools/pens/wxPen.py,sha256=W9RRHlBWHp-CVC4Exvk3ytBmRaB4-LgJPP5Bv7o9BA0,680
302
+ fontTools/qu2cu/__init__.py,sha256=Jfm1JljXbt91w4gyvZn6jzEmVnhRx50sh2fDongrOsE,618
303
+ fontTools/qu2cu/__main__.py,sha256=9FWf6SIZaRaC8SiL0LhjAWC2yIdY9N_9wlRko8m1l2Q,93
304
+ fontTools/qu2cu/__pycache__/__init__.cpython-312.pyc,,
305
+ fontTools/qu2cu/__pycache__/__main__.cpython-312.pyc,,
306
+ fontTools/qu2cu/__pycache__/benchmark.cpython-312.pyc,,
307
+ fontTools/qu2cu/__pycache__/cli.cpython-312.pyc,,
308
+ fontTools/qu2cu/__pycache__/qu2cu.cpython-312.pyc,,
309
+ fontTools/qu2cu/benchmark.py,sha256=GMcr_4r7L6K9SmJ13itt-_XKhnKqSVUDPlXUG6IZmmM,1400
310
+ fontTools/qu2cu/cli.py,sha256=U2rooYnVVEalGRAWGFHk-Kp6Okys8wtzdaWLjw1bngY,3714
311
+ fontTools/qu2cu/qu2cu.c,sha256=1F7GKT21uskZbU01eryh1sg-aTbB_KUXDZnL1JGOahY,692747
312
+ fontTools/qu2cu/qu2cu.cpython-312-x86_64-linux-gnu.so,sha256=BlQPoIhu2uIqyuUaTTfSqDJby0p3yl993im96bJ_YsE,1234352
313
+ fontTools/qu2cu/qu2cu.py,sha256=IYtpkwHdfKOXJr65Y_pJ9Lrt_MgJaISAKGMAs5ilFSM,12288
314
+ fontTools/subset/__init__.py,sha256=R9VoZ2QWhqENHC5Ct1wyhLhEU-xo4mUXwCWl6EZGgwQ,143263
315
+ fontTools/subset/__main__.py,sha256=bhtfP2SqP4k799pxtksFgnC-XGNQDr3LcO4lc8T5e5g,95
316
+ fontTools/subset/__pycache__/__init__.cpython-312.pyc,,
317
+ fontTools/subset/__pycache__/__main__.cpython-312.pyc,,
318
+ fontTools/subset/__pycache__/cff.cpython-312.pyc,,
319
+ fontTools/subset/__pycache__/svg.cpython-312.pyc,,
320
+ fontTools/subset/__pycache__/util.cpython-312.pyc,,
321
+ fontTools/subset/cff.py,sha256=rqMRJOlX5FacV1LW8aDlVOglgEM87TkMA9bdsYenask,6145
322
+ fontTools/subset/svg.py,sha256=sbuR0UHVB_9vYIb6dWCbZ5yCqushkpp5lg-f9NeatBM,9297
323
+ fontTools/subset/util.py,sha256=9SXFYb5Ef9Z58uXmYPCQil8B2i3Q7aFB_1fFDFSppdU,754
324
+ fontTools/svgLib/__init__.py,sha256=IGCLwSbU8jLhq6HI2vSdPQgNs6zDUi5774TgX5MCXPY,75
325
+ fontTools/svgLib/__pycache__/__init__.cpython-312.pyc,,
326
+ fontTools/svgLib/path/__init__.py,sha256=C82fh7xH6ZHsSFVnV848-xeDezpokx1EwTmayJCouFU,1996
327
+ fontTools/svgLib/path/__pycache__/__init__.cpython-312.pyc,,
328
+ fontTools/svgLib/path/__pycache__/arc.cpython-312.pyc,,
329
+ fontTools/svgLib/path/__pycache__/parser.cpython-312.pyc,,
330
+ fontTools/svgLib/path/__pycache__/shapes.cpython-312.pyc,,
331
+ fontTools/svgLib/path/arc.py,sha256=-f5Ym6q4tDWQ76sMNSTUTWgL_7AfgXojvBhtBS7bWwQ,5812
332
+ fontTools/svgLib/path/parser.py,sha256=8T6okMstvgM9ufb2zBcwSzsuuoYbqfnUjNYgb6kjznU,10788
333
+ fontTools/svgLib/path/shapes.py,sha256=xvBUIckKyT9JLy7q_ZP50r6TjvZANyHdZP7wFDzErcI,5322
334
+ fontTools/t1Lib/__init__.py,sha256=p42y70wEIbuX0IIxZG7-b_I-gHto1VLy0gLsDvxCfkw,20865
335
+ fontTools/t1Lib/__pycache__/__init__.cpython-312.pyc,,
336
+ fontTools/tfmLib.py,sha256=UMbkM73JXRJVS9t2B-BJc13rSjImaWBuzCoehLwHFhs,14270
337
+ fontTools/ttLib/__init__.py,sha256=1k7qp9z04gA3m6GvxDaINjqrKbzOkdTA_4RnqW_-LrA,661
338
+ fontTools/ttLib/__main__.py,sha256=lHMPWsnzjKPuMFavf6i1gpk9KexiAk4qzgDd50Mbby0,4733
339
+ fontTools/ttLib/__pycache__/__init__.cpython-312.pyc,,
340
+ fontTools/ttLib/__pycache__/__main__.cpython-312.pyc,,
341
+ fontTools/ttLib/__pycache__/macUtils.cpython-312.pyc,,
342
+ fontTools/ttLib/__pycache__/removeOverlaps.cpython-312.pyc,,
343
+ fontTools/ttLib/__pycache__/reorderGlyphs.cpython-312.pyc,,
344
+ fontTools/ttLib/__pycache__/scaleUpem.cpython-312.pyc,,
345
+ fontTools/ttLib/__pycache__/sfnt.cpython-312.pyc,,
346
+ fontTools/ttLib/__pycache__/standardGlyphOrder.cpython-312.pyc,,
347
+ fontTools/ttLib/__pycache__/ttCollection.cpython-312.pyc,,
348
+ fontTools/ttLib/__pycache__/ttFont.cpython-312.pyc,,
349
+ fontTools/ttLib/__pycache__/ttGlyphSet.cpython-312.pyc,,
350
+ fontTools/ttLib/__pycache__/ttVisitor.cpython-312.pyc,,
351
+ fontTools/ttLib/__pycache__/woff2.cpython-312.pyc,,
352
+ fontTools/ttLib/macUtils.py,sha256=lj3oeFpyjV7ko_JqnluneITmAtlc119J-vwTTg2s73A,1737
353
+ fontTools/ttLib/removeOverlaps.py,sha256=ny3XhFw-RrriJ6n0sSh4SaVaKc3GhmCVZMw8g4oI0TY,12744
354
+ fontTools/ttLib/reorderGlyphs.py,sha256=TbxLxqPTUGiKRX3ulGFCwVm2lEisFYlX6caONJr_4oY,10371
355
+ fontTools/ttLib/scaleUpem.py,sha256=U_-NGkwfS9GRIackdEXjGYZ-wSomcUPXQahDneLeArI,14618
356
+ fontTools/ttLib/sfnt.py,sha256=D5f7kvZX7rXDcmDqVBXqPPki4k27QgiBFYi8ziH2OiU,22981
357
+ fontTools/ttLib/standardGlyphOrder.py,sha256=7AY_fVWdtwZ4iv5uWdyKAUcbEQiSDt1lN4sqx9xXwE0,5785
358
+ fontTools/ttLib/tables/B_A_S_E_.py,sha256=H71A9pJ850mvjbrWHqy8iFI2Dxg7102YRtAkfdCooig,369
359
+ fontTools/ttLib/tables/BitmapGlyphMetrics.py,sha256=9gcGPVzsxEYnVBO7YLWfeOuht9PaCl09GmbAqDYqKi0,1769
360
+ fontTools/ttLib/tables/C_B_D_T_.py,sha256=5LNdc8FMir1kC5fvp5iHwWfeuE-RuqdxAArFXaqPjQ0,3646
361
+ fontTools/ttLib/tables/C_B_L_C_.py,sha256=YXlwovoCHYx8THLQD9iBU_VGoaB9LFObEKtL6ddD320,520
362
+ fontTools/ttLib/tables/C_F_F_.py,sha256=yg3mUtYBudgmpG7Bz475j_DNnCelsgrTsM8DH1uR4ek,1978
363
+ fontTools/ttLib/tables/C_F_F__2.py,sha256=YoHfJQdF-ezx4OwRQ2Y2O7rRJEPjOkf3Hx5Y11Xq0AM,807
364
+ fontTools/ttLib/tables/C_O_L_R_.py,sha256=SHwFVNVmoUQR2e87KuTSe-J9LfeegS4f2hEpee29_2o,5993
365
+ fontTools/ttLib/tables/C_P_A_L_.py,sha256=odFjqM4GnjXyQYGEC-e0Gvqms1jQ5zHHG3SDg7y-BI0,11942
366
+ fontTools/ttLib/tables/D_S_I_G_.py,sha256=AgQPM9Cdro1P-ehJjTfsC9mRTTtSc16At0nnpb1XOGI,5517
367
+ fontTools/ttLib/tables/D__e_b_g.py,sha256=KDnfkNOUnm3F13wD_j3YNBOvYadZ40Gf_0170hFkJp0,1134
368
+ fontTools/ttLib/tables/DefaultTable.py,sha256=cOtgkLWPY9qmOH2BSPt4c4IUSdANWTKx2rK1CTxQ4h0,1487
369
+ fontTools/ttLib/tables/E_B_D_T_.py,sha256=uOpmt25gOJQeO1u1IGAyPWgVmh-4vSZqrQEHvOYwbwg,32534
370
+ fontTools/ttLib/tables/E_B_L_C_.py,sha256=LfEVzBg_yWr9dhChzS0U2G-7wNOwzwB0LWoXIUYNKKM,30054
371
+ fontTools/ttLib/tables/F_F_T_M_.py,sha256=_450vdbEH7Y-0_rOwb3Q0hg-Qq2W8C_sHljy7rZtqqM,1683
372
+ fontTools/ttLib/tables/F__e_a_t.py,sha256=ct79Gf__5ALlqfSBn6wvw6fazb31Od71R6vIp6o9XF4,5483
373
+ fontTools/ttLib/tables/G_D_E_F_.py,sha256=QXiILFCRnPNZcwpub6ojN5S9WP6y56LsXi25pUWLgp4,299
374
+ fontTools/ttLib/tables/G_M_A_P_.py,sha256=fvIQumokOCLa8DFeq_xi069F9RROsXSVmDvWtxgyacQ,4720
375
+ fontTools/ttLib/tables/G_P_K_G_.py,sha256=Xi4Hj2OxZ2IZgVyBQ-Qyiie0hPZjpXZkrao-E5EdTWM,4646
376
+ fontTools/ttLib/tables/G_P_O_S_.py,sha256=UkP3mlnyvQg-jj6ZBOh6j-OieVg_goJQ31nlLvoLGSI,397
377
+ fontTools/ttLib/tables/G_S_U_B_.py,sha256=cwFMKO-pgwsn1H8Q9Jb58Z6ZrBrCoN0sqJB0YunBfSk,294
378
+ fontTools/ttLib/tables/G_V_A_R_.py,sha256=13oO2dD-L4yfkrBuR-KN2rc40wh5lLIlx_khwMz5GH4,94
379
+ fontTools/ttLib/tables/G__l_a_t.py,sha256=Xh3IzFgYlvNjrAOn7Ja73DrWrQTJgJxmDFSUKS6yHdM,8645
380
+ fontTools/ttLib/tables/G__l_o_c.py,sha256=5DsxGzaG7HyJVvLlKQeff1lXt-XPWaHNNaf-EYwsKh4,2685
381
+ fontTools/ttLib/tables/H_V_A_R_.py,sha256=6kPLDUGT8EussA3y9gKr_mrgY5PNv7YaK1V0keMXD9w,313
382
+ fontTools/ttLib/tables/J_S_T_F_.py,sha256=Q9TEf3OuyDIxZlmoz9a3c-mDMlJK6YBQ9KcYmiwFRbU,315
383
+ fontTools/ttLib/tables/L_T_S_H_.py,sha256=Iu6syJFuhJj0_7Aan2NPlDuQDIq-AzLwsOQbXVTnlL0,2189
384
+ fontTools/ttLib/tables/M_A_T_H_.py,sha256=-TVu9Nlcs-1shkElbIk-CWtUwXUMdycHFkjvPE8C_fs,342
385
+ fontTools/ttLib/tables/M_E_T_A_.py,sha256=sA6ookcjchw8UYVEuS8QEXc62I9_Rms9cu_jKA6MkNI,11989
386
+ fontTools/ttLib/tables/M_V_A_R_.py,sha256=67cEuiTw5y5W1Zk98L_S_SmJINIfy_mzWCkyHcujz94,308
387
+ fontTools/ttLib/tables/O_S_2f_2.py,sha256=1Pq2Xu4oYOJePTHC_hTKg3RIfKely3j6T1u_lMTEpD8,28030
388
+ fontTools/ttLib/tables/S_I_N_G_.py,sha256=CFDy8R2fDeYn7ocfrZr7Ui7U9D0h4G55CdPfY55g-Bk,3317
389
+ fontTools/ttLib/tables/S_T_A_T_.py,sha256=y9NiWCtnlZtMjw4K9_SdA84Xa-dJk7G5eb2dSe6ciWc,498
390
+ fontTools/ttLib/tables/S_V_G_.py,sha256=vT6QTW5ArtskVUxnPEH_ZxKz4DF4v1pKbylN6DG0R3o,7676
391
+ fontTools/ttLib/tables/S__i_l_f.py,sha256=lPQV2RdhcJRgfDzHp_dkgSxVUUdkcAnY1Bz7V18Gt9U,34985
392
+ fontTools/ttLib/tables/S__i_l_l.py,sha256=Vjtn7SI83vaLGIuQf2e-jhZSFOXb9vXB4jwqznjqnMc,3224
393
+ fontTools/ttLib/tables/T_S_I_B_.py,sha256=3WhEtyNnjYumcowD0GpjubrgnS-RzouZxCxEe4yLDo8,341
394
+ fontTools/ttLib/tables/T_S_I_C_.py,sha256=hAV9Hq_ALsWaducDpw1tDRREvFL7hx7onnUF0sXTelU,381
395
+ fontTools/ttLib/tables/T_S_I_D_.py,sha256=TsdX-G2xxVQO9sSE1wE_xDRx-gor5YiXTHeUthMwCPY,341
396
+ fontTools/ttLib/tables/T_S_I_J_.py,sha256=x8Tlvi6aTxoQcI12UL7muoWF1Q61iBDseAS1mRdOYrg,341
397
+ fontTools/ttLib/tables/T_S_I_P_.py,sha256=-il2ucTBOghVBY7cmleHdLZc3W3CKh7-iPPT0A3KBzk,341
398
+ fontTools/ttLib/tables/T_S_I_S_.py,sha256=tVBnl63vyZUIq93oM6dEjHCXvPn9vt5vvL3jG59b0Lg,341
399
+ fontTools/ttLib/tables/T_S_I_V_.py,sha256=iUWxz2MSrtw7mzuQZj30QAJrCPnyJ4GincFfySFUNAg,855
400
+ fontTools/ttLib/tables/T_S_I__0.py,sha256=O-2oI0eBgt4mP15-UwH0_0r7YWi3EEEhG-4etqDueGI,2505
401
+ fontTools/ttLib/tables/T_S_I__1.py,sha256=nSUhni-fvYmeKXW4zLfP3FG_3LQU2QKPKS1_gKY5lYg,6971
402
+ fontTools/ttLib/tables/T_S_I__2.py,sha256=q2rub-d77iWWiBM6awO0-TCl-Xq7kalPobHYC2QEOfc,496
403
+ fontTools/ttLib/tables/T_S_I__3.py,sha256=0LcvvCzVZJzyz7i4zjIkUuYXEqXwOCs9WeCsgDFqKJ8,543
404
+ fontTools/ttLib/tables/T_S_I__5.py,sha256=hhvJn6jiXs8kuBtun8krNUTXTljH-eKxaxXM1T-7SXM,1905
405
+ fontTools/ttLib/tables/T_T_F_A_.py,sha256=LuT0w__AMtawnsBMobhEMW9gp2yk0mA5ZRzwF45c0UI,392
406
+ fontTools/ttLib/tables/TupleVariation.py,sha256=4XTDTRPZWPg9_1K5SVgdNoxtgQvahtiO4LNO7fk1cK4,32235
407
+ fontTools/ttLib/tables/V_A_R_C_.py,sha256=3jFX50J6X-Cc4dwwiztKKsDTRXVHTXlVdQH328UN1-k,289
408
+ fontTools/ttLib/tables/V_D_M_X_.py,sha256=RbHl7vvO9pcjT_kKvcCmcByQj39n4PmVeq55wD5C14g,10437
409
+ fontTools/ttLib/tables/V_O_R_G_.py,sha256=Cn3OxjVtcO-Uvp61P5c2336V9iEbuGr6vWAXnSIaihk,5965
410
+ fontTools/ttLib/tables/V_V_A_R_.py,sha256=Cstw6tc_U4-EmTriRItBSpvTJODAjMFQjfyTaxLzsbI,319
411
+ fontTools/ttLib/tables/__init__.py,sha256=eQPcuHCfRuGtt6nOa0KwV6vtUNKHnwuQyA7xSN8SPoc,2651
412
+ fontTools/ttLib/tables/__pycache__/B_A_S_E_.cpython-312.pyc,,
413
+ fontTools/ttLib/tables/__pycache__/BitmapGlyphMetrics.cpython-312.pyc,,
414
+ fontTools/ttLib/tables/__pycache__/C_B_D_T_.cpython-312.pyc,,
415
+ fontTools/ttLib/tables/__pycache__/C_B_L_C_.cpython-312.pyc,,
416
+ fontTools/ttLib/tables/__pycache__/C_F_F_.cpython-312.pyc,,
417
+ fontTools/ttLib/tables/__pycache__/C_F_F__2.cpython-312.pyc,,
418
+ fontTools/ttLib/tables/__pycache__/C_O_L_R_.cpython-312.pyc,,
419
+ fontTools/ttLib/tables/__pycache__/C_P_A_L_.cpython-312.pyc,,
420
+ fontTools/ttLib/tables/__pycache__/D_S_I_G_.cpython-312.pyc,,
421
+ fontTools/ttLib/tables/__pycache__/D__e_b_g.cpython-312.pyc,,
422
+ fontTools/ttLib/tables/__pycache__/DefaultTable.cpython-312.pyc,,
423
+ fontTools/ttLib/tables/__pycache__/E_B_D_T_.cpython-312.pyc,,
424
+ fontTools/ttLib/tables/__pycache__/E_B_L_C_.cpython-312.pyc,,
425
+ fontTools/ttLib/tables/__pycache__/F_F_T_M_.cpython-312.pyc,,
426
+ fontTools/ttLib/tables/__pycache__/F__e_a_t.cpython-312.pyc,,
427
+ fontTools/ttLib/tables/__pycache__/G_D_E_F_.cpython-312.pyc,,
428
+ fontTools/ttLib/tables/__pycache__/G_M_A_P_.cpython-312.pyc,,
429
+ fontTools/ttLib/tables/__pycache__/G_P_K_G_.cpython-312.pyc,,
430
+ fontTools/ttLib/tables/__pycache__/G_P_O_S_.cpython-312.pyc,,
431
+ fontTools/ttLib/tables/__pycache__/G_S_U_B_.cpython-312.pyc,,
432
+ fontTools/ttLib/tables/__pycache__/G_V_A_R_.cpython-312.pyc,,
433
+ fontTools/ttLib/tables/__pycache__/G__l_a_t.cpython-312.pyc,,
434
+ fontTools/ttLib/tables/__pycache__/G__l_o_c.cpython-312.pyc,,
435
+ fontTools/ttLib/tables/__pycache__/H_V_A_R_.cpython-312.pyc,,
436
+ fontTools/ttLib/tables/__pycache__/J_S_T_F_.cpython-312.pyc,,
437
+ fontTools/ttLib/tables/__pycache__/L_T_S_H_.cpython-312.pyc,,
438
+ fontTools/ttLib/tables/__pycache__/M_A_T_H_.cpython-312.pyc,,
439
+ fontTools/ttLib/tables/__pycache__/M_E_T_A_.cpython-312.pyc,,
440
+ fontTools/ttLib/tables/__pycache__/M_V_A_R_.cpython-312.pyc,,
441
+ fontTools/ttLib/tables/__pycache__/O_S_2f_2.cpython-312.pyc,,
442
+ fontTools/ttLib/tables/__pycache__/S_I_N_G_.cpython-312.pyc,,
443
+ fontTools/ttLib/tables/__pycache__/S_T_A_T_.cpython-312.pyc,,
444
+ fontTools/ttLib/tables/__pycache__/S_V_G_.cpython-312.pyc,,
445
+ fontTools/ttLib/tables/__pycache__/S__i_l_f.cpython-312.pyc,,
446
+ fontTools/ttLib/tables/__pycache__/S__i_l_l.cpython-312.pyc,,
447
+ fontTools/ttLib/tables/__pycache__/T_S_I_B_.cpython-312.pyc,,
448
+ fontTools/ttLib/tables/__pycache__/T_S_I_C_.cpython-312.pyc,,
449
+ fontTools/ttLib/tables/__pycache__/T_S_I_D_.cpython-312.pyc,,
450
+ fontTools/ttLib/tables/__pycache__/T_S_I_J_.cpython-312.pyc,,
451
+ fontTools/ttLib/tables/__pycache__/T_S_I_P_.cpython-312.pyc,,
452
+ fontTools/ttLib/tables/__pycache__/T_S_I_S_.cpython-312.pyc,,
453
+ fontTools/ttLib/tables/__pycache__/T_S_I_V_.cpython-312.pyc,,
454
+ fontTools/ttLib/tables/__pycache__/T_S_I__0.cpython-312.pyc,,
455
+ fontTools/ttLib/tables/__pycache__/T_S_I__1.cpython-312.pyc,,
456
+ fontTools/ttLib/tables/__pycache__/T_S_I__2.cpython-312.pyc,,
457
+ fontTools/ttLib/tables/__pycache__/T_S_I__3.cpython-312.pyc,,
458
+ fontTools/ttLib/tables/__pycache__/T_S_I__5.cpython-312.pyc,,
459
+ fontTools/ttLib/tables/__pycache__/T_T_F_A_.cpython-312.pyc,,
460
+ fontTools/ttLib/tables/__pycache__/TupleVariation.cpython-312.pyc,,
461
+ fontTools/ttLib/tables/__pycache__/V_A_R_C_.cpython-312.pyc,,
462
+ fontTools/ttLib/tables/__pycache__/V_D_M_X_.cpython-312.pyc,,
463
+ fontTools/ttLib/tables/__pycache__/V_O_R_G_.cpython-312.pyc,,
464
+ fontTools/ttLib/tables/__pycache__/V_V_A_R_.cpython-312.pyc,,
465
+ fontTools/ttLib/tables/__pycache__/__init__.cpython-312.pyc,,
466
+ fontTools/ttLib/tables/__pycache__/_a_n_k_r.cpython-312.pyc,,
467
+ fontTools/ttLib/tables/__pycache__/_a_v_a_r.cpython-312.pyc,,
468
+ fontTools/ttLib/tables/__pycache__/_b_s_l_n.cpython-312.pyc,,
469
+ fontTools/ttLib/tables/__pycache__/_c_i_d_g.cpython-312.pyc,,
470
+ fontTools/ttLib/tables/__pycache__/_c_m_a_p.cpython-312.pyc,,
471
+ fontTools/ttLib/tables/__pycache__/_c_v_a_r.cpython-312.pyc,,
472
+ fontTools/ttLib/tables/__pycache__/_c_v_t.cpython-312.pyc,,
473
+ fontTools/ttLib/tables/__pycache__/_f_e_a_t.cpython-312.pyc,,
474
+ fontTools/ttLib/tables/__pycache__/_f_p_g_m.cpython-312.pyc,,
475
+ fontTools/ttLib/tables/__pycache__/_f_v_a_r.cpython-312.pyc,,
476
+ fontTools/ttLib/tables/__pycache__/_g_a_s_p.cpython-312.pyc,,
477
+ fontTools/ttLib/tables/__pycache__/_g_c_i_d.cpython-312.pyc,,
478
+ fontTools/ttLib/tables/__pycache__/_g_l_y_f.cpython-312.pyc,,
479
+ fontTools/ttLib/tables/__pycache__/_g_v_a_r.cpython-312.pyc,,
480
+ fontTools/ttLib/tables/__pycache__/_h_d_m_x.cpython-312.pyc,,
481
+ fontTools/ttLib/tables/__pycache__/_h_e_a_d.cpython-312.pyc,,
482
+ fontTools/ttLib/tables/__pycache__/_h_h_e_a.cpython-312.pyc,,
483
+ fontTools/ttLib/tables/__pycache__/_h_m_t_x.cpython-312.pyc,,
484
+ fontTools/ttLib/tables/__pycache__/_k_e_r_n.cpython-312.pyc,,
485
+ fontTools/ttLib/tables/__pycache__/_l_c_a_r.cpython-312.pyc,,
486
+ fontTools/ttLib/tables/__pycache__/_l_o_c_a.cpython-312.pyc,,
487
+ fontTools/ttLib/tables/__pycache__/_l_t_a_g.cpython-312.pyc,,
488
+ fontTools/ttLib/tables/__pycache__/_m_a_x_p.cpython-312.pyc,,
489
+ fontTools/ttLib/tables/__pycache__/_m_e_t_a.cpython-312.pyc,,
490
+ fontTools/ttLib/tables/__pycache__/_m_o_r_t.cpython-312.pyc,,
491
+ fontTools/ttLib/tables/__pycache__/_m_o_r_x.cpython-312.pyc,,
492
+ fontTools/ttLib/tables/__pycache__/_n_a_m_e.cpython-312.pyc,,
493
+ fontTools/ttLib/tables/__pycache__/_o_p_b_d.cpython-312.pyc,,
494
+ fontTools/ttLib/tables/__pycache__/_p_o_s_t.cpython-312.pyc,,
495
+ fontTools/ttLib/tables/__pycache__/_p_r_e_p.cpython-312.pyc,,
496
+ fontTools/ttLib/tables/__pycache__/_p_r_o_p.cpython-312.pyc,,
497
+ fontTools/ttLib/tables/__pycache__/_s_b_i_x.cpython-312.pyc,,
498
+ fontTools/ttLib/tables/__pycache__/_t_r_a_k.cpython-312.pyc,,
499
+ fontTools/ttLib/tables/__pycache__/_v_h_e_a.cpython-312.pyc,,
500
+ fontTools/ttLib/tables/__pycache__/_v_m_t_x.cpython-312.pyc,,
501
+ fontTools/ttLib/tables/__pycache__/asciiTable.cpython-312.pyc,,
502
+ fontTools/ttLib/tables/__pycache__/grUtils.cpython-312.pyc,,
503
+ fontTools/ttLib/tables/__pycache__/otBase.cpython-312.pyc,,
504
+ fontTools/ttLib/tables/__pycache__/otConverters.cpython-312.pyc,,
505
+ fontTools/ttLib/tables/__pycache__/otData.cpython-312.pyc,,
506
+ fontTools/ttLib/tables/__pycache__/otTables.cpython-312.pyc,,
507
+ fontTools/ttLib/tables/__pycache__/otTraverse.cpython-312.pyc,,
508
+ fontTools/ttLib/tables/__pycache__/sbixGlyph.cpython-312.pyc,,
509
+ fontTools/ttLib/tables/__pycache__/sbixStrike.cpython-312.pyc,,
510
+ fontTools/ttLib/tables/__pycache__/ttProgram.cpython-312.pyc,,
511
+ fontTools/ttLib/tables/_a_n_k_r.py,sha256=MpAzIifmIi_3gx2oP6PC3R2lu36Ewsr2-W1rXjsz2Ug,483
512
+ fontTools/ttLib/tables/_a_v_a_r.py,sha256=_qplWEwFos0VwRTdZa7LhfLIWw-CF1G76NbiRM_Rgvs,7374
513
+ fontTools/ttLib/tables/_b_s_l_n.py,sha256=_848o7SQqztzBDfHYei-80u9ltxIHVBzXu1dYHLV57M,465
514
+ fontTools/ttLib/tables/_c_i_d_g.py,sha256=yt8rVIadpJSDUCoVH4dZetNiy0Azm5ESAxHjB2BX_eA,913
515
+ fontTools/ttLib/tables/_c_m_a_p.py,sha256=r8-bB_E0EQh5h4TGX5nTnDnwTUtXuRB3iuqEDoN_IOM,62202
516
+ fontTools/ttLib/tables/_c_v_a_r.py,sha256=35ayk2kX1pcLGwyx0y4I1l-r7LHgdKv0ulVx8oBPteI,3527
517
+ fontTools/ttLib/tables/_c_v_t.py,sha256=1_RhEcTmhWQWQp7Hsj8UsByKmXCIppZyIbIArGywEEM,1618
518
+ fontTools/ttLib/tables/_f_e_a_t.py,sha256=Fi1XnjhkCG0tp43AcvpIaivD-YRFpufo6feGIrenQDo,469
519
+ fontTools/ttLib/tables/_f_p_g_m.py,sha256=uZHZzqL6OdLn_Hxskv-xf3XuE4fyaSv_jbALEjwXYug,1633
520
+ fontTools/ttLib/tables/_f_v_a_r.py,sha256=rV33H2BgHUl3Wuydsou1G-Hi4uASBppWaLj3FMmiLjs,8837
521
+ fontTools/ttLib/tables/_g_a_s_p.py,sha256=YvhAVDvdssN2fjPMTfSrO4WBCfTuh9T2cU5zquDVnSw,2203
522
+ fontTools/ttLib/tables/_g_c_i_d.py,sha256=AJ4uV7PTHbnsw4Tfw8c2Ezh0VMox3oAH0qhhq7y8hdM,362
523
+ fontTools/ttLib/tables/_g_l_y_f.py,sha256=dDV65llsEDI9fKcVKC5TOiaXpXSyMHNEytuYOGt7adM,85584
524
+ fontTools/ttLib/tables/_g_v_a_r.py,sha256=E9WCKjeITUfd5hcJLQ0rjQFBtZdxw1eswFlWp1U6bD4,12196
525
+ fontTools/ttLib/tables/_h_d_m_x.py,sha256=wMrO4D04QNT8u30p8AV-aG3bndXCq4wlPNvtbd8ip7c,4252
526
+ fontTools/ttLib/tables/_h_e_a_d.py,sha256=yY2GTFq6Mn6nN8EegbMVJRMUWIqDYFln3FhTk3ziw6s,4926
527
+ fontTools/ttLib/tables/_h_h_e_a.py,sha256=X4t1aF1MZMuz3phCVSFwKcNTeoZdx-042wFtHc-nK9w,4767
528
+ fontTools/ttLib/tables/_h_m_t_x.py,sha256=rbxr3cy9-9Jm0HCGIWQiX6fGH5iu6yojp9kfgWrW2Ks,6192
529
+ fontTools/ttLib/tables/_k_e_r_n.py,sha256=DQNLmD_HEdDKPfp4tamOd9W3T5a1lXFM5tDaWrKl164,10794
530
+ fontTools/ttLib/tables/_l_c_a_r.py,sha256=8W6xFOj-sm003MCXX4bIHxs9ntfVvT0FXYllPxa3z4I,390
531
+ fontTools/ttLib/tables/_l_o_c_a.py,sha256=yxiwLKXLZjNju5XYmLb6EhNLec1d7ezEDDe1dszceHo,2180
532
+ fontTools/ttLib/tables/_l_t_a_g.py,sha256=9YpApjI-rZ4e3HeT8Pj-osiHl3uALD9JXg5O7pqk9L0,2552
533
+ fontTools/ttLib/tables/_m_a_x_p.py,sha256=cIDIZWse9czwwsnlxIh3qwgwaXbt7PQAjXKAcmMDspY,5264
534
+ fontTools/ttLib/tables/_m_e_t_a.py,sha256=A0CZPEAVxYrpytjXUGQJCTddwG8KrvUVbtBe3A1MqgI,3913
535
+ fontTools/ttLib/tables/_m_o_r_t.py,sha256=u35tYqn3cjzKxeCF0FUFeLtaf36mjDDSN08uuk0Kme8,487
536
+ fontTools/ttLib/tables/_m_o_r_x.py,sha256=OwamVpIO7REDnFr95HuFPoY_0U6i9zQPb11K1sFTvDY,548
537
+ fontTools/ttLib/tables/_n_a_m_e.py,sha256=86_0fUeA5_c-GY5ZnkqUI0jyWwMh1mn6yVOf6KKqIlU,41266
538
+ fontTools/ttLib/tables/_o_p_b_d.py,sha256=TNZv_2YTrj4dGzd6wA9Jb-KGZ99un177s5p3LlfxQ74,448
539
+ fontTools/ttLib/tables/_p_o_s_t.py,sha256=9siVXSisWGdTfj_mC1E9dUDz9Jdm1i3QzI-l3i3VWME,11671
540
+ fontTools/ttLib/tables/_p_r_e_p.py,sha256=CcKr4HrswkupLmbJdrJLTM-z9XgLefQyv8467j9V0zs,427
541
+ fontTools/ttLib/tables/_p_r_o_p.py,sha256=Eg8x5qWyXDzPezMafFu0s0qyPDHj-sPsFxGtE6h29qo,427
542
+ fontTools/ttLib/tables/_s_b_i_x.py,sha256=tkkKbNKNYkUhZJuN0kl7q37x5KK5OovB06y28obPV6A,4865
543
+ fontTools/ttLib/tables/_t_r_a_k.py,sha256=rrrPZLELFYA5F8PERoafIS9cb_d_i6xtpAzHEbsFHSw,11379
544
+ fontTools/ttLib/tables/_v_h_e_a.py,sha256=FuULIBl4OQyUeLPOFEY8buB0pAnQhGa1-5a6kN9i5Sc,4459
545
+ fontTools/ttLib/tables/_v_m_t_x.py,sha256=AUuxtyQvMWrTBNbOIaL6uKcB_DNpNb0YX28JIuTHw_Y,500
546
+ fontTools/ttLib/tables/asciiTable.py,sha256=4c69jsAirUnDEpylf9CYBoCKTzwbmfbtUAOrtPnpHjY,637
547
+ fontTools/ttLib/tables/grUtils.py,sha256=hcOJ5oJPOd2uJWnWA7qwR7AfL37YZ5zUT7g8o5BBV80,2270
548
+ fontTools/ttLib/tables/otBase.py,sha256=k1Mt5sLd2EL6ufX3e0ZaBDgTvSLCUKOW6qy5UDeKwxI,52986
549
+ fontTools/ttLib/tables/otConverters.py,sha256=ihE_WMSKAKSaBbMvnFYDj2eMxf7PvRMMa8zGwfoYuYc,74202
550
+ fontTools/ttLib/tables/otData.py,sha256=-XXRwdVfP-Wz7oBjMPpku0A0QH9lw_fFGNzZlt9N0mo,197262
551
+ fontTools/ttLib/tables/otTables.py,sha256=2U04ot_2ITlBZx2QtpnIOtBGftPFs9ZX2FWfz4vz1G0,96987
552
+ fontTools/ttLib/tables/otTraverse.py,sha256=HznEVAlVf_8eyqjsO2edgELtMlXnjnUqccK3PytvVUE,5518
553
+ fontTools/ttLib/tables/sbixGlyph.py,sha256=tjEUPVRfx6gr5yme8UytGTtVrimKN5qmbzT1GZPjXiM,5796
554
+ fontTools/ttLib/tables/sbixStrike.py,sha256=dL8O9K8R4S6RVQDP-PVjIPBrvbqbE9zwra0uRL0nLq0,6651
555
+ fontTools/ttLib/tables/table_API_readme.txt,sha256=eZlRTLUkLzc_9Ot3pdfhyMb3ahU0_Iipx0vSbzOVGy8,2748
556
+ fontTools/ttLib/tables/ttProgram.py,sha256=tgtxgd-EnOq-2PUlYEihp-6NHu_7HnE5rxeSAtmXOtU,35888
557
+ fontTools/ttLib/ttCollection.py,sha256=aRph2MkBK3kd9-JCLqhJ1EN9pffN_lVX6WWmOTTewc8,3963
558
+ fontTools/ttLib/ttFont.py,sha256=TKcK7qSvxHum0iXpX0zS3YiDjt6yW9wlK_YsoP2torE,63707
559
+ fontTools/ttLib/ttGlyphSet.py,sha256=cUBhMGa5hszeVqOm2KpOdeJh-LsiqE7RNdyIUPZ2vO8,17476
560
+ fontTools/ttLib/ttVisitor.py,sha256=_tah4C42Tv6Pm9QeLNQwwVCxqI4VNEAqYCbmThp6cvY,1025
561
+ fontTools/ttLib/woff2.py,sha256=6LPISeBQ1dubzKjWrUcYm_vgETC46BTLY4XkG52qvSA,60921
562
+ fontTools/ttx.py,sha256=FxuGubujWCGJWSTrJEjoNH--25fVIPy-ZRtYy9H6iTk,17277
563
+ fontTools/ufoLib/__init__.py,sha256=nKG8gu6NEvqGJoZ781IARoQ7ii4LoWfMMvX3Yf5TsVw,98981
564
+ fontTools/ufoLib/__pycache__/__init__.cpython-312.pyc,,
565
+ fontTools/ufoLib/__pycache__/converters.cpython-312.pyc,,
566
+ fontTools/ufoLib/__pycache__/errors.cpython-312.pyc,,
567
+ fontTools/ufoLib/__pycache__/etree.cpython-312.pyc,,
568
+ fontTools/ufoLib/__pycache__/filenames.cpython-312.pyc,,
569
+ fontTools/ufoLib/__pycache__/glifLib.cpython-312.pyc,,
570
+ fontTools/ufoLib/__pycache__/kerning.cpython-312.pyc,,
571
+ fontTools/ufoLib/__pycache__/plistlib.cpython-312.pyc,,
572
+ fontTools/ufoLib/__pycache__/pointPen.cpython-312.pyc,,
573
+ fontTools/ufoLib/__pycache__/utils.cpython-312.pyc,,
574
+ fontTools/ufoLib/__pycache__/validators.cpython-312.pyc,,
575
+ fontTools/ufoLib/converters.py,sha256=YnBKr8kmyjwLcq8LdD46ubGOgyL9Pxt9avlvZn9anKI,13444
576
+ fontTools/ufoLib/errors.py,sha256=9f8l5NaFAj3BZPa6Bbqt06FL4afffLuMzy4nPf-eOlE,845
577
+ fontTools/ufoLib/etree.py,sha256=T3sjLTgjMAq6VyYRicWPaMIVBJ2YSuwZxV6Vc5yZtQI,231
578
+ fontTools/ufoLib/filenames.py,sha256=hoyUhzzQMDaeckT7UdreISANq4-gLR2jGyk5yAyYtOA,10654
579
+ fontTools/ufoLib/glifLib.py,sha256=Y-xzf4qbTIOl3-dVLXvu3iFCIDtAEu_klId2_UNngWs,77170
580
+ fontTools/ufoLib/kerning.py,sha256=o1BeJDVZ_CZZPzmOPwRKTqglYmhA_JZPjwq2JLgdQIk,4836
581
+ fontTools/ufoLib/plistlib.py,sha256=jzMGOGvHO6XvS-IO8hS04ur7r8-v2dnVq-vKMoJZvqQ,1510
582
+ fontTools/ufoLib/pointPen.py,sha256=CuREcm3IYteZNBDAd_ZRAV4XqBsy0s07jdWc4en9r-8,244
583
+ fontTools/ufoLib/utils.py,sha256=nZoJJqHXQSL-LXYE58_WHA97XlbTkEbYkdH3GL32SmQ,3192
584
+ fontTools/ufoLib/validators.py,sha256=MWBqcLThGyYpst61QothA_BSlc6jGVhPvFiay-pobCY,32387
585
+ fontTools/unicode.py,sha256=ZZ7OMmWvIyV1IL1k6ioTzaRAh3tUvm6gvK7QgFbOIHY,1237
586
+ fontTools/unicodedata/Blocks.py,sha256=frShN07WqD1uLt-V0TNlUZPnn0-cKc0Nuhtd2uNDoSw,33139
587
+ fontTools/unicodedata/Mirrored.py,sha256=kdhwCWOWaArmfNkDah0Thv-67M9wWz45R5IMPhqyzFM,9242
588
+ fontTools/unicodedata/OTTags.py,sha256=wOPpbMsNcp_gdvPFeITtgVMnTN8TJSNAsVEdu_nuPXE,1196
589
+ fontTools/unicodedata/ScriptExtensions.py,sha256=udL53Wdd5n-ZFEVHeIC0LpKKBqPHD2WqRiBbzXs2U7U,28647
590
+ fontTools/unicodedata/Scripts.py,sha256=wBD6SElfM3w-u6JcgyNsyofXPvqW1e4YltgeHE4XaOc,131148
591
+ fontTools/unicodedata/__init__.py,sha256=4eR0Luk4QJHC9YDG0_wi6UrIDawED36e6Xbf3S5hDPg,9085
592
+ fontTools/unicodedata/__pycache__/Blocks.cpython-312.pyc,,
593
+ fontTools/unicodedata/__pycache__/Mirrored.cpython-312.pyc,,
594
+ fontTools/unicodedata/__pycache__/OTTags.cpython-312.pyc,,
595
+ fontTools/unicodedata/__pycache__/ScriptExtensions.cpython-312.pyc,,
596
+ fontTools/unicodedata/__pycache__/Scripts.cpython-312.pyc,,
597
+ fontTools/unicodedata/__pycache__/__init__.cpython-312.pyc,,
598
+ fontTools/varLib/__init__.py,sha256=nzk6FSRozAMz9CDVqhMVYhh4Ho_hjIKrYtkMIJLn0y0,57467
599
+ fontTools/varLib/__main__.py,sha256=wbdYC5bPjWCxA0I4SKcLO88gl-UMtsYS8MxdW9ySTkY,95
600
+ fontTools/varLib/__pycache__/__init__.cpython-312.pyc,,
601
+ fontTools/varLib/__pycache__/__main__.cpython-312.pyc,,
602
+ fontTools/varLib/__pycache__/avarPlanner.cpython-312.pyc,,
603
+ fontTools/varLib/__pycache__/builder.cpython-312.pyc,,
604
+ fontTools/varLib/__pycache__/cff.cpython-312.pyc,,
605
+ fontTools/varLib/__pycache__/errors.cpython-312.pyc,,
606
+ fontTools/varLib/__pycache__/featureVars.cpython-312.pyc,,
607
+ fontTools/varLib/__pycache__/hvar.cpython-312.pyc,,
608
+ fontTools/varLib/__pycache__/interpolatable.cpython-312.pyc,,
609
+ fontTools/varLib/__pycache__/interpolatableHelpers.cpython-312.pyc,,
610
+ fontTools/varLib/__pycache__/interpolatablePlot.cpython-312.pyc,,
611
+ fontTools/varLib/__pycache__/interpolatableTestContourOrder.cpython-312.pyc,,
612
+ fontTools/varLib/__pycache__/interpolatableTestStartingPoint.cpython-312.pyc,,
613
+ fontTools/varLib/__pycache__/interpolate_layout.cpython-312.pyc,,
614
+ fontTools/varLib/__pycache__/iup.cpython-312.pyc,,
615
+ fontTools/varLib/__pycache__/merger.cpython-312.pyc,,
616
+ fontTools/varLib/__pycache__/models.cpython-312.pyc,,
617
+ fontTools/varLib/__pycache__/multiVarStore.cpython-312.pyc,,
618
+ fontTools/varLib/__pycache__/mutator.cpython-312.pyc,,
619
+ fontTools/varLib/__pycache__/mvar.cpython-312.pyc,,
620
+ fontTools/varLib/__pycache__/plot.cpython-312.pyc,,
621
+ fontTools/varLib/__pycache__/stat.cpython-312.pyc,,
622
+ fontTools/varLib/__pycache__/varStore.cpython-312.pyc,,
623
+ fontTools/varLib/avar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
624
+ fontTools/varLib/avar/__main__.py,sha256=ew1fJpg81GpYbdkrp4I7ntWdbhkDeg5fNn1SrpF254k,1770
625
+ fontTools/varLib/avar/__pycache__/__init__.cpython-312.pyc,,
626
+ fontTools/varLib/avar/__pycache__/__main__.cpython-312.pyc,,
627
+ fontTools/varLib/avar/__pycache__/build.cpython-312.pyc,,
628
+ fontTools/varLib/avar/__pycache__/map.cpython-312.pyc,,
629
+ fontTools/varLib/avar/__pycache__/plan.cpython-312.pyc,,
630
+ fontTools/varLib/avar/__pycache__/unbuild.cpython-312.pyc,,
631
+ fontTools/varLib/avar/build.py,sha256=YSMsRMjGkNW2zhKn29HgovNNG-jgiFSsK7zYH4un2Ws,2087
632
+ fontTools/varLib/avar/map.py,sha256=UBeElT40SHXvJrQ7SflH5MNYKTTom7RLHwq2varb_oE,2867
633
+ fontTools/varLib/avar/plan.py,sha256=qDKZjQq6OfeXBr3T3DZnrmauyMm5ek0rm5YVosAO_UA,27354
634
+ fontTools/varLib/avar/unbuild.py,sha256=h3qb0JBN7SngijPrwjTjGEjueeENlhPYY9FyN02FLEk,10467
635
+ fontTools/varLib/avarPlanner.py,sha256=CabG5xB57FMdwmN1acpVMvyOucVPxCdRdJSK2gu7gb4,109
636
+ fontTools/varLib/builder.py,sha256=mSKOCcnnw-WzmZs15FayoqCDh77Ts7o9Tre9psh8CUc,6609
637
+ fontTools/varLib/cff.py,sha256=EVgaQcoROIrYQsRuftnxFuGGldEPYbrIh5yBckylJC4,22901
638
+ fontTools/varLib/errors.py,sha256=dMo8eGj76I7H4hrBEiNbYrGs2J1K1SwdsUyTHpkVOrQ,6934
639
+ fontTools/varLib/featureVars.py,sha256=kp4gPjKyyGRu7yBWxgd1N4OkKnU9V45QwiWSHs6OWd0,26180
640
+ fontTools/varLib/hvar.py,sha256=1IvL5BneTkg8jJYicH0TSQViB6D0vBEesLdlfqoLBX4,3695
641
+ fontTools/varLib/instancer/__init__.py,sha256=KVXejhVDoIx8cqQuJKOB_N2q8uE2stsMQ68tqp268PI,75502
642
+ fontTools/varLib/instancer/__main__.py,sha256=zfULwcP01FhplS1IlcMgNQnLxk5RVfmOuinWjqeid-g,104
643
+ fontTools/varLib/instancer/__pycache__/__init__.cpython-312.pyc,,
644
+ fontTools/varLib/instancer/__pycache__/__main__.cpython-312.pyc,,
645
+ fontTools/varLib/instancer/__pycache__/featureVars.cpython-312.pyc,,
646
+ fontTools/varLib/instancer/__pycache__/names.cpython-312.pyc,,
647
+ fontTools/varLib/instancer/__pycache__/solver.cpython-312.pyc,,
648
+ fontTools/varLib/instancer/featureVars.py,sha256=oPqSlnHLMDTtOsmQMi6gkzLox7ymCrqlRAkvC_EJ4bc,7110
649
+ fontTools/varLib/instancer/names.py,sha256=IPRqel_M8zVU0jl30WsfgufxUm9PBBQDQCY3VHapeHc,14950
650
+ fontTools/varLib/instancer/solver.py,sha256=uMePwX0BVT5F94kUvDglsI4_F0nEH67F7RFuJ6tQwQ0,11002
651
+ fontTools/varLib/interpolatable.py,sha256=Bhlq_LhEZ-sXfLNY8aFEChFrsKuT2kzmnuMfG5qi0v4,45221
652
+ fontTools/varLib/interpolatableHelpers.py,sha256=cTFgTqDjggSCqNfTM77__5b9Sja_g7xWWMiB-pXDx84,11672
653
+ fontTools/varLib/interpolatablePlot.py,sha256=w393P6mGLRhYkIjSxMww3qyoYxAUZzCXlmPBbI_84C0,44375
654
+ fontTools/varLib/interpolatableTestContourOrder.py,sha256=mHJ9Ry7Rm7H3zHDwEUQEtEIDseiUzOxjg4MveW_FSiU,3021
655
+ fontTools/varLib/interpolatableTestStartingPoint.py,sha256=K6OYKBspim6BXc91pfLTbGLyi5XZukfMuBc6hRpENG8,4296
656
+ fontTools/varLib/interpolate_layout.py,sha256=22VjGZuV2YiAe2MpdTf0xPVz1x2G84bcOL0vOeBpGQM,3689
657
+ fontTools/varLib/iup.c,sha256=64aSGfKmZAN_uGJrYxvtmzstksJYD3ak2iv8iyjW3bM,834812
658
+ fontTools/varLib/iup.cpython-312-x86_64-linux-gnu.so,sha256=avtYY7S7fCAM2bP8jjTATzKZ2dGVBRc98J22k_fxPxU,1650280
659
+ fontTools/varLib/iup.py,sha256=mKq_GRWuUg4yTmw2V32nu0v2r-SzzN7xS7rIbV0mYuc,14984
660
+ fontTools/varLib/merger.py,sha256=E59oli4AwqWZ-FgnuStMSBvsB-FHe-55esXTYUqGeJ8,60802
661
+ fontTools/varLib/models.py,sha256=mkwlucmKDkyhPZWEpab7ofcuA19hGlqjHtY9MwmYtOI,22697
662
+ fontTools/varLib/multiVarStore.py,sha256=eQEuWNY01YF5zDpy1UwNtvOYyD6c0FLxpH-QFpX1i78,8305
663
+ fontTools/varLib/mutator.py,sha256=kzXiLFxRLgU2pcHzOzh9u0n0KkO3DuBk06xZ_RPhWz8,19804
664
+ fontTools/varLib/mvar.py,sha256=LTV77vH_3Ecg_qKBO5xQzjLOlJir_ppEr7mPVZRgad8,2449
665
+ fontTools/varLib/plot.py,sha256=NoSZkJ5ndxNcDvJIvd5pQ9_jX6X1oM1K2G_tR4sdPVs,7494
666
+ fontTools/varLib/stat.py,sha256=XuNKKZxGlBrl4OGFDAwVXhpBwJi23U3BdHmNTKoJnvE,4811
667
+ fontTools/varLib/varStore.py,sha256=2QA9SDI6jQyQ_zq82OOwa3FBkfl-ksaSo1KGmVFpa9Q,24069
668
+ fontTools/voltLib/__init__.py,sha256=ZZ1AsTx1VlDn40Kupce-fM3meOWugy3RZraBW9LG-9M,151
669
+ fontTools/voltLib/__main__.py,sha256=uVtABLzMeHtvKL8zetf4rpC4aB8BkYr5QLSegNjZZZI,5928
670
+ fontTools/voltLib/__pycache__/__init__.cpython-312.pyc,,
671
+ fontTools/voltLib/__pycache__/__main__.cpython-312.pyc,,
672
+ fontTools/voltLib/__pycache__/ast.cpython-312.pyc,,
673
+ fontTools/voltLib/__pycache__/error.cpython-312.pyc,,
674
+ fontTools/voltLib/__pycache__/lexer.cpython-312.pyc,,
675
+ fontTools/voltLib/__pycache__/parser.cpython-312.pyc,,
676
+ fontTools/voltLib/__pycache__/voltToFea.cpython-312.pyc,,
677
+ fontTools/voltLib/ast.py,sha256=arA9W3Gqo6OqljwNNKnMAojz-C5LStbC5SgjJh7buKk,13300
678
+ fontTools/voltLib/error.py,sha256=phcQOQj-xOspCXu9hBJQRhSOBDzxHRgZd3fWQOFNJzw,395
679
+ fontTools/voltLib/lexer.py,sha256=OvuETOSvlS6v7iCVeJ3IdH2Cg71n3OJoEyiB3-h6vhE,3368
680
+ fontTools/voltLib/parser.py,sha256=rkw2IHBZPsrhGVC7Kw7V501m0u52kh1JSM5HXp-xchM,25396
681
+ fontTools/voltLib/voltToFea.py,sha256=Z2yvnaZLQXzPLT86Uta0zRsXIYgj6NnvZtSWt5xmw2s,36549
682
+ fonttools-4.61.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
683
+ fonttools-4.61.1.dist-info/METADATA,sha256=sF0h1Ul5KoQam4cNjcDOXJ1D2Z1nYh_xN5sYvop8OUA,114185
684
+ fonttools-4.61.1.dist-info/RECORD,,
685
+ fonttools-4.61.1.dist-info/WHEEL,sha256=kaf3GlniTxElItJKanA5i_DdhhelZhGNqtfvwRkHPG0,224
686
+ fonttools-4.61.1.dist-info/entry_points.txt,sha256=8kVHddxfFWA44FSD4mBpmC-4uCynQnkoz_9aNJb227Y,147
687
+ fonttools-4.61.1.dist-info/licenses/LICENSE,sha256=Z4cgj4P2Wcy8IiOy_elS_6b36KymLxqKK_W8UbsbI4M,1072
688
+ fonttools-4.61.1.dist-info/licenses/LICENSE.external,sha256=lKg6ruBymg8wLTSsxKzsvZ1YNm8mJCkHX-VX5KVLLmk,20022
689
+ fonttools-4.61.1.dist-info/top_level.txt,sha256=rRgRylrXzekqWOsrhygzib12pQ7WILf7UGjqEwkIFDM,10
lib/python3.12/site-packages/fonttools-4.61.1.dist-info/WHEEL ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-cp312-manylinux_2_5_x86_64
5
+ Tag: cp312-cp312-manylinux1_x86_64
6
+ Tag: cp312-cp312-manylinux_2_17_x86_64
7
+ Tag: cp312-cp312-manylinux2014_x86_64
8
+
lib/python3.12/site-packages/fonttools-4.61.1.dist-info/entry_points.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ [console_scripts]
2
+ fonttools = fontTools.__main__:main
3
+ pyftmerge = fontTools.merge:main
4
+ pyftsubset = fontTools.subset:main
5
+ ttx = fontTools.ttx:main
lib/python3.12/site-packages/fonttools-4.61.1.dist-info/licenses/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Just van Rossum
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
lib/python3.12/site-packages/fonttools-4.61.1.dist-info/licenses/LICENSE.external ADDED
@@ -0,0 +1,388 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FontTools includes the following font projects for testing purposes, which are
2
+ under SIL Open Font License, Version 1.1:
3
+
4
+ Lobster
5
+ Copyright (c) 2010, Pablo Impallari (www.impallari.com|impallari@gmail.com),
6
+ with Reserved Font Name Lobster.
7
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
8
+
9
+ Noto Fonts
10
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
11
+
12
+ XITS font project
13
+ Copyright (c) 2001-2010 by the STI Pub Companies, consisting of the American
14
+ Institute of Physics, the American Chemical Society, the American
15
+ Mathematical Society, the American Physical Society, Elsevier, Inc., and The
16
+ Institute of Electrical and Electronic Engineers, Inc. (www.stixfonts.org),
17
+ with Reserved Font Name STIX Fonts, STIX Fonts (TM) is a trademark of The
18
+ Institute of Electrical and Electronics Engineers, Inc.
19
+
20
+ Portions copyright (c) 1998-2003 by MicroPress, Inc.
21
+ (www.micropress-inc.com), with Reserved Font Name TM Math. To obtain
22
+ additional mathematical fonts, please contact MicroPress, Inc., 68-30 Harrow
23
+ Street, Forest Hills, NY 11375, USA, Phone: (718) 575-1816.
24
+
25
+ Portions copyright (c) 1990 by Elsevier, Inc.
26
+
27
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
28
+
29
+ Iosevka
30
+ Copyright (c) 2015-2020 Belleve Invis (belleve@typeof.net).
31
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
32
+
33
+ This license is copied below, and is also available with a FAQ at:
34
+ http://scripts.sil.org/OFL
35
+
36
+ -----------------------------------------------------------
37
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
38
+ -----------------------------------------------------------
39
+
40
+ PREAMBLE
41
+ The goals of the Open Font License (OFL) are to stimulate worldwide
42
+ development of collaborative font projects, to support the font
43
+ creation efforts of academic and linguistic communities, and to
44
+ provide a free and open framework in which fonts may be shared and
45
+ improved in partnership with others.
46
+
47
+ The OFL allows the licensed fonts to be used, studied, modified and
48
+ redistributed freely as long as they are not sold by themselves. The
49
+ fonts, including any derivative works, can be bundled, embedded,
50
+ redistributed and/or sold with any software provided that any reserved
51
+ names are not used by derivative works. The fonts and derivatives,
52
+ however, cannot be released under any other type of license. The
53
+ requirement for fonts to remain under this license does not apply to
54
+ any document created using the fonts or their derivatives.
55
+
56
+ DEFINITIONS
57
+ "Font Software" refers to the set of files released by the Copyright
58
+ Holder(s) under this license and clearly marked as such. This may
59
+ include source files, build scripts and documentation.
60
+
61
+ "Reserved Font Name" refers to any names specified as such after the
62
+ copyright statement(s).
63
+
64
+ "Original Version" refers to the collection of Font Software
65
+ components as distributed by the Copyright Holder(s).
66
+
67
+ "Modified Version" refers to any derivative made by adding to,
68
+ deleting, or substituting -- in part or in whole -- any of the
69
+ components of the Original Version, by changing formats or by porting
70
+ the Font Software to a new environment.
71
+
72
+ "Author" refers to any designer, engineer, programmer, technical
73
+ writer or other person who contributed to the Font Software.
74
+
75
+ PERMISSION & CONDITIONS
76
+ Permission is hereby granted, free of charge, to any person obtaining
77
+ a copy of the Font Software, to use, study, copy, merge, embed,
78
+ modify, redistribute, and sell modified and unmodified copies of the
79
+ Font Software, subject to the following conditions:
80
+
81
+ 1) Neither the Font Software nor any of its individual components, in
82
+ Original or Modified Versions, may be sold by itself.
83
+
84
+ 2) Original or Modified Versions of the Font Software may be bundled,
85
+ redistributed and/or sold with any software, provided that each copy
86
+ contains the above copyright notice and this license. These can be
87
+ included either as stand-alone text files, human-readable headers or
88
+ in the appropriate machine-readable metadata fields within text or
89
+ binary files as long as those fields can be easily viewed by the user.
90
+
91
+ 3) No Modified Version of the Font Software may use the Reserved Font
92
+ Name(s) unless explicit written permission is granted by the
93
+ corresponding Copyright Holder. This restriction only applies to the
94
+ primary font name as presented to the users.
95
+
96
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
97
+ Software shall not be used to promote, endorse or advertise any
98
+ Modified Version, except to acknowledge the contribution(s) of the
99
+ Copyright Holder(s) and the Author(s) or with their explicit written
100
+ permission.
101
+
102
+ 5) The Font Software, modified or unmodified, in part or in whole,
103
+ must be distributed entirely under this license, and must not be
104
+ distributed under any other license. The requirement for fonts to
105
+ remain under this license does not apply to any document created using
106
+ the Font Software.
107
+
108
+ TERMINATION
109
+ This license becomes null and void if any of the above conditions are
110
+ not met.
111
+
112
+ DISCLAIMER
113
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
114
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
115
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
116
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
117
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
118
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
119
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
120
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
121
+ OTHER DEALINGS IN THE FONT SOFTWARE.
122
+
123
+ =====
124
+
125
+ FontTools includes Adobe AGL & AGLFN, which is under 3-clauses BSD license:
126
+
127
+ Redistribution and use in source and binary forms, with or without
128
+ modification, are permitted provided that the following conditions are
129
+ met:
130
+
131
+ Redistributions of source code must retain the above copyright notice,
132
+ this list of conditions and the following disclaimer.
133
+
134
+ Redistributions in binary form must reproduce the above copyright
135
+ notice, this list of conditions and the following disclaimer in the
136
+ documentation and/or other materials provided with the distribution.
137
+
138
+ Neither the name of Adobe Systems Incorporated nor the names of its
139
+ contributors may be used to endorse or promote products derived from
140
+ this software without specific prior written permission.
141
+
142
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
143
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
144
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
145
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
146
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
147
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
148
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
149
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
150
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
151
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
152
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
153
+
154
+ =====
155
+
156
+ FontTools includes cu2qu, which is Copyright 2016 Google Inc. All Rights Reserved.
157
+ Licensed under the Apache License, Version 2.0, a copy of which is reproduced below:
158
+
159
+ Apache License
160
+ Version 2.0, January 2004
161
+ http://www.apache.org/licenses/
162
+
163
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
164
+
165
+ 1. Definitions.
166
+
167
+ "License" shall mean the terms and conditions for use, reproduction,
168
+ and distribution as defined by Sections 1 through 9 of this document.
169
+
170
+ "Licensor" shall mean the copyright owner or entity authorized by
171
+ the copyright owner that is granting the License.
172
+
173
+ "Legal Entity" shall mean the union of the acting entity and all
174
+ other entities that control, are controlled by, or are under common
175
+ control with that entity. For the purposes of this definition,
176
+ "control" means (i) the power, direct or indirect, to cause the
177
+ direction or management of such entity, whether by contract or
178
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
179
+ outstanding shares, or (iii) beneficial ownership of such entity.
180
+
181
+ "You" (or "Your") shall mean an individual or Legal Entity
182
+ exercising permissions granted by this License.
183
+
184
+ "Source" form shall mean the preferred form for making modifications,
185
+ including but not limited to software source code, documentation
186
+ source, and configuration files.
187
+
188
+ "Object" form shall mean any form resulting from mechanical
189
+ transformation or translation of a Source form, including but
190
+ not limited to compiled object code, generated documentation,
191
+ and conversions to other media types.
192
+
193
+ "Work" shall mean the work of authorship, whether in Source or
194
+ Object form, made available under the License, as indicated by a
195
+ copyright notice that is included in or attached to the work
196
+ (an example is provided in the Appendix below).
197
+
198
+ "Derivative Works" shall mean any work, whether in Source or Object
199
+ form, that is based on (or derived from) the Work and for which the
200
+ editorial revisions, annotations, elaborations, or other modifications
201
+ represent, as a whole, an original work of authorship. For the purposes
202
+ of this License, Derivative Works shall not include works that remain
203
+ separable from, or merely link (or bind by name) to the interfaces of,
204
+ the Work and Derivative Works thereof.
205
+
206
+ "Contribution" shall mean any work of authorship, including
207
+ the original version of the Work and any modifications or additions
208
+ to that Work or Derivative Works thereof, that is intentionally
209
+ submitted to Licensor for inclusion in the Work by the copyright owner
210
+ or by an individual or Legal Entity authorized to submit on behalf of
211
+ the copyright owner. For the purposes of this definition, "submitted"
212
+ means any form of electronic, verbal, or written communication sent
213
+ to the Licensor or its representatives, including but not limited to
214
+ communication on electronic mailing lists, source code control systems,
215
+ and issue tracking systems that are managed by, or on behalf of, the
216
+ Licensor for the purpose of discussing and improving the Work, but
217
+ excluding communication that is conspicuously marked or otherwise
218
+ designated in writing by the copyright owner as "Not a Contribution."
219
+
220
+ "Contributor" shall mean Licensor and any individual or Legal Entity
221
+ on behalf of whom a Contribution has been received by Licensor and
222
+ subsequently incorporated within the Work.
223
+
224
+ 2. Grant of Copyright License. Subject to the terms and conditions of
225
+ this License, each Contributor hereby grants to You a perpetual,
226
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
227
+ copyright license to reproduce, prepare Derivative Works of,
228
+ publicly display, publicly perform, sublicense, and distribute the
229
+ Work and such Derivative Works in Source or Object form.
230
+
231
+ 3. Grant of Patent License. Subject to the terms and conditions of
232
+ this License, each Contributor hereby grants to You a perpetual,
233
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
234
+ (except as stated in this section) patent license to make, have made,
235
+ use, offer to sell, sell, import, and otherwise transfer the Work,
236
+ where such license applies only to those patent claims licensable
237
+ by such Contributor that are necessarily infringed by their
238
+ Contribution(s) alone or by combination of their Contribution(s)
239
+ with the Work to which such Contribution(s) was submitted. If You
240
+ institute patent litigation against any entity (including a
241
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
242
+ or a Contribution incorporated within the Work constitutes direct
243
+ or contributory patent infringement, then any patent licenses
244
+ granted to You under this License for that Work shall terminate
245
+ as of the date such litigation is filed.
246
+
247
+ 4. Redistribution. You may reproduce and distribute copies of the
248
+ Work or Derivative Works thereof in any medium, with or without
249
+ modifications, and in Source or Object form, provided that You
250
+ meet the following conditions:
251
+
252
+ (a) You must give any other recipients of the Work or
253
+ Derivative Works a copy of this License; and
254
+
255
+ (b) You must cause any modified files to carry prominent notices
256
+ stating that You changed the files; and
257
+
258
+ (c) You must retain, in the Source form of any Derivative Works
259
+ that You distribute, all copyright, patent, trademark, and
260
+ attribution notices from the Source form of the Work,
261
+ excluding those notices that do not pertain to any part of
262
+ the Derivative Works; and
263
+
264
+ (d) If the Work includes a "NOTICE" text file as part of its
265
+ distribution, then any Derivative Works that You distribute must
266
+ include a readable copy of the attribution notices contained
267
+ within such NOTICE file, excluding those notices that do not
268
+ pertain to any part of the Derivative Works, in at least one
269
+ of the following places: within a NOTICE text file distributed
270
+ as part of the Derivative Works; within the Source form or
271
+ documentation, if provided along with the Derivative Works; or,
272
+ within a display generated by the Derivative Works, if and
273
+ wherever such third-party notices normally appear. The contents
274
+ of the NOTICE file are for informational purposes only and
275
+ do not modify the License. You may add Your own attribution
276
+ notices within Derivative Works that You distribute, alongside
277
+ or as an addendum to the NOTICE text from the Work, provided
278
+ that such additional attribution notices cannot be construed
279
+ as modifying the License.
280
+
281
+ You may add Your own copyright statement to Your modifications and
282
+ may provide additional or different license terms and conditions
283
+ for use, reproduction, or distribution of Your modifications, or
284
+ for any such Derivative Works as a whole, provided Your use,
285
+ reproduction, and distribution of the Work otherwise complies with
286
+ the conditions stated in this License.
287
+
288
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
289
+ any Contribution intentionally submitted for inclusion in the Work
290
+ by You to the Licensor shall be under the terms and conditions of
291
+ this License, without any additional terms or conditions.
292
+ Notwithstanding the above, nothing herein shall supersede or modify
293
+ the terms of any separate license agreement you may have executed
294
+ with Licensor regarding such Contributions.
295
+
296
+ 6. Trademarks. This License does not grant permission to use the trade
297
+ names, trademarks, service marks, or product names of the Licensor,
298
+ except as required for reasonable and customary use in describing the
299
+ origin of the Work and reproducing the content of the NOTICE file.
300
+
301
+ 7. Disclaimer of Warranty. Unless required by applicable law or
302
+ agreed to in writing, Licensor provides the Work (and each
303
+ Contributor provides its Contributions) on an "AS IS" BASIS,
304
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
305
+ implied, including, without limitation, any warranties or conditions
306
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
307
+ PARTICULAR PURPOSE. You are solely responsible for determining the
308
+ appropriateness of using or redistributing the Work and assume any
309
+ risks associated with Your exercise of permissions under this License.
310
+
311
+ 8. Limitation of Liability. In no event and under no legal theory,
312
+ whether in tort (including negligence), contract, or otherwise,
313
+ unless required by applicable law (such as deliberate and grossly
314
+ negligent acts) or agreed to in writing, shall any Contributor be
315
+ liable to You for damages, including any direct, indirect, special,
316
+ incidental, or consequential damages of any character arising as a
317
+ result of this License or out of the use or inability to use the
318
+ Work (including but not limited to damages for loss of goodwill,
319
+ work stoppage, computer failure or malfunction, or any and all
320
+ other commercial damages or losses), even if such Contributor
321
+ has been advised of the possibility of such damages.
322
+
323
+ 9. Accepting Warranty or Additional Liability. While redistributing
324
+ the Work or Derivative Works thereof, You may choose to offer,
325
+ and charge a fee for, acceptance of support, warranty, indemnity,
326
+ or other liability obligations and/or rights consistent with this
327
+ License. However, in accepting such obligations, You may act only
328
+ on Your own behalf and on Your sole responsibility, not on behalf
329
+ of any other Contributor, and only if You agree to indemnify,
330
+ defend, and hold each Contributor harmless for any liability
331
+ incurred by, or claims asserted against, such Contributor by reason
332
+ of your accepting any such warranty or additional liability.
333
+
334
+ END OF TERMS AND CONDITIONS
335
+
336
+ APPENDIX: How to apply the Apache License to your work.
337
+
338
+ To apply the Apache License to your work, attach the following
339
+ boilerplate notice, with the fields enclosed by brackets "[]"
340
+ replaced with your own identifying information. (Don't include
341
+ the brackets!) The text should be enclosed in the appropriate
342
+ comment syntax for the file format. We also recommend that a
343
+ file or class name and description of purpose be included on the
344
+ same "printed page" as the copyright notice for easier
345
+ identification within third-party archives.
346
+
347
+ Copyright [yyyy] [name of copyright owner]
348
+
349
+ Licensed under the Apache License, Version 2.0 (the "License");
350
+ you may not use this file except in compliance with the License.
351
+ You may obtain a copy of the License at
352
+
353
+ http://www.apache.org/licenses/LICENSE-2.0
354
+
355
+ Unless required by applicable law or agreed to in writing, software
356
+ distributed under the License is distributed on an "AS IS" BASIS,
357
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
358
+ See the License for the specific language governing permissions and
359
+ limitations under the License.
360
+
361
+ =====
362
+
363
+ FontTools includes code in `fontTools.misc.filesystem` which is derived from:
364
+
365
+ PyFilesystem2 (i.e. the `fs` package) by Will McGugan
366
+ Licensed under the MIT License
367
+ https://github.com/PyFilesystem/pyfilesystem2
368
+
369
+ Copyright (c) 2017-2021 The PyFilesystem2 contributors
370
+ Copyright (c) 2016-2019 Will McGugan
371
+
372
+ Permission is hereby granted, free of charge, to any person obtaining a copy
373
+ of this software and associated documentation files (the "Software"), to deal
374
+ in the Software without restriction, including without limitation the rights
375
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
376
+ copies of the Software, and to permit persons to whom the Software is
377
+ furnished to do so, subject to the following conditions:
378
+
379
+ The above copyright notice and this permission notice shall be included in all
380
+ copies or substantial portions of the Software.
381
+
382
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
383
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
384
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
385
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
386
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
387
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
388
+ SOFTWARE.
lib/python3.12/site-packages/fonttools-4.61.1.dist-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ fontTools
lib/python3.12/site-packages/gitdb/__init__.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ """Initialize the object database module"""
6
+
7
+ __author__ = "Sebastian Thiel"
8
+ __contact__ = "byronimo@gmail.com"
9
+ __homepage__ = "https://github.com/gitpython-developers/gitdb"
10
+ version_info = (4, 0, 12)
11
+ __version__ = '.'.join(str(i) for i in version_info)
12
+
13
+ # default imports
14
+ from gitdb.base import *
15
+ from gitdb.db import *
16
+ from gitdb.stream import *
lib/python3.12/site-packages/gitdb/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (767 Bytes). View file
 
lib/python3.12/site-packages/gitdb/__pycache__/base.cpython-312.pyc ADDED
Binary file (12.8 kB). View file
 
lib/python3.12/site-packages/gitdb/__pycache__/const.cpython-312.pyc ADDED
Binary file (334 Bytes). View file
 
lib/python3.12/site-packages/gitdb/__pycache__/exc.cpython-312.pyc ADDED
Binary file (2.59 kB). View file
 
lib/python3.12/site-packages/gitdb/__pycache__/fun.cpython-312.pyc ADDED
Binary file (23.7 kB). View file
 
lib/python3.12/site-packages/gitdb/__pycache__/pack.cpython-312.pyc ADDED
Binary file (44.5 kB). View file
 
lib/python3.12/site-packages/gitdb/__pycache__/stream.cpython-312.pyc ADDED
Binary file (26.7 kB). View file
 
lib/python3.12/site-packages/gitdb/__pycache__/typ.cpython-312.pyc ADDED
Binary file (394 Bytes). View file
 
lib/python3.12/site-packages/gitdb/__pycache__/util.cpython-312.pyc ADDED
Binary file (15.2 kB). View file
 
lib/python3.12/site-packages/gitdb/base.py ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ """Module with basic data structures - they are designed to be lightweight and fast"""
6
+ from gitdb.util import bin_to_hex
7
+
8
+ from gitdb.fun import (
9
+ type_id_to_type_map,
10
+ type_to_type_id_map
11
+ )
12
+
13
+ __all__ = ('OInfo', 'OPackInfo', 'ODeltaPackInfo',
14
+ 'OStream', 'OPackStream', 'ODeltaPackStream',
15
+ 'IStream', 'InvalidOInfo', 'InvalidOStream')
16
+
17
+ #{ ODB Bases
18
+
19
+
20
+ class OInfo(tuple):
21
+
22
+ """Carries information about an object in an ODB, providing information
23
+ about the binary sha of the object, the type_string as well as the uncompressed size
24
+ in bytes.
25
+
26
+ It can be accessed using tuple notation and using attribute access notation::
27
+
28
+ assert dbi[0] == dbi.binsha
29
+ assert dbi[1] == dbi.type
30
+ assert dbi[2] == dbi.size
31
+
32
+ The type is designed to be as lightweight as possible."""
33
+ __slots__ = tuple()
34
+
35
+ def __new__(cls, sha, type, size):
36
+ return tuple.__new__(cls, (sha, type, size))
37
+
38
+ def __init__(self, *args):
39
+ tuple.__init__(self)
40
+
41
+ #{ Interface
42
+ @property
43
+ def binsha(self):
44
+ """:return: our sha as binary, 20 bytes"""
45
+ return self[0]
46
+
47
+ @property
48
+ def hexsha(self):
49
+ """:return: our sha, hex encoded, 40 bytes"""
50
+ return bin_to_hex(self[0])
51
+
52
+ @property
53
+ def type(self):
54
+ return self[1]
55
+
56
+ @property
57
+ def type_id(self):
58
+ return type_to_type_id_map[self[1]]
59
+
60
+ @property
61
+ def size(self):
62
+ return self[2]
63
+ #} END interface
64
+
65
+
66
+ class OPackInfo(tuple):
67
+
68
+ """As OInfo, but provides a type_id property to retrieve the numerical type id, and
69
+ does not include a sha.
70
+
71
+ Additionally, the pack_offset is the absolute offset into the packfile at which
72
+ all object information is located. The data_offset property points to the absolute
73
+ location in the pack at which that actual data stream can be found."""
74
+ __slots__ = tuple()
75
+
76
+ def __new__(cls, packoffset, type, size):
77
+ return tuple.__new__(cls, (packoffset, type, size))
78
+
79
+ def __init__(self, *args):
80
+ tuple.__init__(self)
81
+
82
+ #{ Interface
83
+
84
+ @property
85
+ def pack_offset(self):
86
+ return self[0]
87
+
88
+ @property
89
+ def type(self):
90
+ return type_id_to_type_map[self[1]]
91
+
92
+ @property
93
+ def type_id(self):
94
+ return self[1]
95
+
96
+ @property
97
+ def size(self):
98
+ return self[2]
99
+
100
+ #} END interface
101
+
102
+
103
+ class ODeltaPackInfo(OPackInfo):
104
+
105
+ """Adds delta specific information,
106
+ Either the 20 byte sha which points to some object in the database,
107
+ or the negative offset from the pack_offset, so that pack_offset - delta_info yields
108
+ the pack offset of the base object"""
109
+ __slots__ = tuple()
110
+
111
+ def __new__(cls, packoffset, type, size, delta_info):
112
+ return tuple.__new__(cls, (packoffset, type, size, delta_info))
113
+
114
+ #{ Interface
115
+ @property
116
+ def delta_info(self):
117
+ return self[3]
118
+ #} END interface
119
+
120
+
121
+ class OStream(OInfo):
122
+
123
+ """Base for object streams retrieved from the database, providing additional
124
+ information about the stream.
125
+ Generally, ODB streams are read-only as objects are immutable"""
126
+ __slots__ = tuple()
127
+
128
+ def __new__(cls, sha, type, size, stream, *args, **kwargs):
129
+ """Helps with the initialization of subclasses"""
130
+ return tuple.__new__(cls, (sha, type, size, stream))
131
+
132
+ def __init__(self, *args, **kwargs):
133
+ tuple.__init__(self)
134
+
135
+ #{ Stream Reader Interface
136
+
137
+ def read(self, size=-1):
138
+ return self[3].read(size)
139
+
140
+ @property
141
+ def stream(self):
142
+ return self[3]
143
+
144
+ #} END stream reader interface
145
+
146
+
147
+ class ODeltaStream(OStream):
148
+
149
+ """Uses size info of its stream, delaying reads"""
150
+
151
+ def __new__(cls, sha, type, size, stream, *args, **kwargs):
152
+ """Helps with the initialization of subclasses"""
153
+ return tuple.__new__(cls, (sha, type, size, stream))
154
+
155
+ #{ Stream Reader Interface
156
+
157
+ @property
158
+ def size(self):
159
+ return self[3].size
160
+
161
+ #} END stream reader interface
162
+
163
+
164
+ class OPackStream(OPackInfo):
165
+
166
+ """Next to pack object information, a stream outputting an undeltified base object
167
+ is provided"""
168
+ __slots__ = tuple()
169
+
170
+ def __new__(cls, packoffset, type, size, stream, *args):
171
+ """Helps with the initialization of subclasses"""
172
+ return tuple.__new__(cls, (packoffset, type, size, stream))
173
+
174
+ #{ Stream Reader Interface
175
+ def read(self, size=-1):
176
+ return self[3].read(size)
177
+
178
+ @property
179
+ def stream(self):
180
+ return self[3]
181
+ #} END stream reader interface
182
+
183
+
184
+ class ODeltaPackStream(ODeltaPackInfo):
185
+
186
+ """Provides a stream outputting the uncompressed offset delta information"""
187
+ __slots__ = tuple()
188
+
189
+ def __new__(cls, packoffset, type, size, delta_info, stream):
190
+ return tuple.__new__(cls, (packoffset, type, size, delta_info, stream))
191
+
192
+ #{ Stream Reader Interface
193
+ def read(self, size=-1):
194
+ return self[4].read(size)
195
+
196
+ @property
197
+ def stream(self):
198
+ return self[4]
199
+ #} END stream reader interface
200
+
201
+
202
+ class IStream(list):
203
+
204
+ """Represents an input content stream to be fed into the ODB. It is mutable to allow
205
+ the ODB to record information about the operations outcome right in this instance.
206
+
207
+ It provides interfaces for the OStream and a StreamReader to allow the instance
208
+ to blend in without prior conversion.
209
+
210
+ The only method your content stream must support is 'read'"""
211
+ __slots__ = tuple()
212
+
213
+ def __new__(cls, type, size, stream, sha=None):
214
+ return list.__new__(cls, (sha, type, size, stream, None))
215
+
216
+ def __init__(self, type, size, stream, sha=None):
217
+ list.__init__(self, (sha, type, size, stream, None))
218
+
219
+ #{ Interface
220
+ @property
221
+ def hexsha(self):
222
+ """:return: our sha, hex encoded, 40 bytes"""
223
+ return bin_to_hex(self[0])
224
+
225
+ def _error(self):
226
+ """:return: the error that occurred when processing the stream, or None"""
227
+ return self[4]
228
+
229
+ def _set_error(self, exc):
230
+ """Set this input stream to the given exc, may be None to reset the error"""
231
+ self[4] = exc
232
+
233
+ error = property(_error, _set_error)
234
+
235
+ #} END interface
236
+
237
+ #{ Stream Reader Interface
238
+
239
+ def read(self, size=-1):
240
+ """Implements a simple stream reader interface, passing the read call on
241
+ to our internal stream"""
242
+ return self[3].read(size)
243
+
244
+ #} END stream reader interface
245
+
246
+ #{ interface
247
+
248
+ def _set_binsha(self, binsha):
249
+ self[0] = binsha
250
+
251
+ def _binsha(self):
252
+ return self[0]
253
+
254
+ binsha = property(_binsha, _set_binsha)
255
+
256
+ def _type(self):
257
+ return self[1]
258
+
259
+ def _set_type(self, type):
260
+ self[1] = type
261
+
262
+ type = property(_type, _set_type)
263
+
264
+ def _size(self):
265
+ return self[2]
266
+
267
+ def _set_size(self, size):
268
+ self[2] = size
269
+
270
+ size = property(_size, _set_size)
271
+
272
+ def _stream(self):
273
+ return self[3]
274
+
275
+ def _set_stream(self, stream):
276
+ self[3] = stream
277
+
278
+ stream = property(_stream, _set_stream)
279
+
280
+ #} END odb info interface
281
+
282
+
283
+ class InvalidOInfo(tuple):
284
+
285
+ """Carries information about a sha identifying an object which is invalid in
286
+ the queried database. The exception attribute provides more information about
287
+ the cause of the issue"""
288
+ __slots__ = tuple()
289
+
290
+ def __new__(cls, sha, exc):
291
+ return tuple.__new__(cls, (sha, exc))
292
+
293
+ def __init__(self, sha, exc):
294
+ tuple.__init__(self, (sha, exc))
295
+
296
+ @property
297
+ def binsha(self):
298
+ return self[0]
299
+
300
+ @property
301
+ def hexsha(self):
302
+ return bin_to_hex(self[0])
303
+
304
+ @property
305
+ def error(self):
306
+ """:return: exception instance explaining the failure"""
307
+ return self[1]
308
+
309
+
310
+ class InvalidOStream(InvalidOInfo):
311
+
312
+ """Carries information about an invalid ODB stream"""
313
+ __slots__ = tuple()
314
+
315
+ #} END ODB Bases
lib/python3.12/site-packages/gitdb/const.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ BYTE_SPACE = b' '
2
+ NULL_BYTE = b'\0'
3
+ NULL_HEX_SHA = "0" * 40
4
+ NULL_BIN_SHA = NULL_BYTE * 20
lib/python3.12/site-packages/gitdb/db/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+
6
+ from gitdb.db.base import *
7
+ from gitdb.db.loose import *
8
+ from gitdb.db.mem import *
9
+ from gitdb.db.pack import *
10
+ from gitdb.db.git import *
11
+ from gitdb.db.ref import *
lib/python3.12/site-packages/gitdb/db/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (362 Bytes). View file
 
lib/python3.12/site-packages/gitdb/db/__pycache__/base.cpython-312.pyc ADDED
Binary file (12.1 kB). View file
 
lib/python3.12/site-packages/gitdb/db/__pycache__/git.cpython-312.pyc ADDED
Binary file (3.34 kB). View file
 
lib/python3.12/site-packages/gitdb/db/__pycache__/loose.cpython-312.pyc ADDED
Binary file (9.28 kB). View file
 
lib/python3.12/site-packages/gitdb/db/__pycache__/mem.cpython-312.pyc ADDED
Binary file (4.56 kB). View file
 
lib/python3.12/site-packages/gitdb/db/__pycache__/pack.cpython-312.pyc ADDED
Binary file (8.28 kB). View file
 
lib/python3.12/site-packages/gitdb/db/__pycache__/ref.cpython-312.pyc ADDED
Binary file (3.22 kB). View file
 
lib/python3.12/site-packages/gitdb/db/base.py ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ """Contains implementations of database retrieveing objects"""
6
+ from gitdb.util import (
7
+ join,
8
+ LazyMixin,
9
+ hex_to_bin
10
+ )
11
+
12
+ from gitdb.utils.encoding import force_text
13
+ from gitdb.exc import (
14
+ BadObject,
15
+ AmbiguousObjectName
16
+ )
17
+
18
+ from itertools import chain
19
+ from functools import reduce
20
+
21
+
22
+ __all__ = ('ObjectDBR', 'ObjectDBW', 'FileDBBase', 'CompoundDB', 'CachingDB')
23
+
24
+
25
+ class ObjectDBR:
26
+
27
+ """Defines an interface for object database lookup.
28
+ Objects are identified either by their 20 byte bin sha"""
29
+
30
+ def __contains__(self, sha):
31
+ return self.has_obj
32
+
33
+ #{ Query Interface
34
+ def has_object(self, sha):
35
+ """
36
+ Whether the object identified by the given 20 bytes
37
+ binary sha is contained in the database
38
+
39
+ :return: True if the object identified by the given 20 bytes
40
+ binary sha is contained in the database"""
41
+ raise NotImplementedError("To be implemented in subclass")
42
+
43
+ def info(self, sha):
44
+ """ :return: OInfo instance
45
+ :param sha: bytes binary sha
46
+ :raise BadObject:"""
47
+ raise NotImplementedError("To be implemented in subclass")
48
+
49
+ def stream(self, sha):
50
+ """:return: OStream instance
51
+ :param sha: 20 bytes binary sha
52
+ :raise BadObject:"""
53
+ raise NotImplementedError("To be implemented in subclass")
54
+
55
+ def size(self):
56
+ """:return: amount of objects in this database"""
57
+ raise NotImplementedError()
58
+
59
+ def sha_iter(self):
60
+ """Return iterator yielding 20 byte shas for all objects in this data base"""
61
+ raise NotImplementedError()
62
+
63
+ #} END query interface
64
+
65
+
66
+ class ObjectDBW:
67
+
68
+ """Defines an interface to create objects in the database"""
69
+
70
+ def __init__(self, *args, **kwargs):
71
+ self._ostream = None
72
+
73
+ #{ Edit Interface
74
+ def set_ostream(self, stream):
75
+ """
76
+ Adjusts the stream to which all data should be sent when storing new objects
77
+
78
+ :param stream: if not None, the stream to use, if None the default stream
79
+ will be used.
80
+ :return: previously installed stream, or None if there was no override
81
+ :raise TypeError: if the stream doesn't have the supported functionality"""
82
+ cstream = self._ostream
83
+ self._ostream = stream
84
+ return cstream
85
+
86
+ def ostream(self):
87
+ """
88
+ Return the output stream
89
+
90
+ :return: overridden output stream this instance will write to, or None
91
+ if it will write to the default stream"""
92
+ return self._ostream
93
+
94
+ def store(self, istream):
95
+ """
96
+ Create a new object in the database
97
+ :return: the input istream object with its sha set to its corresponding value
98
+
99
+ :param istream: IStream compatible instance. If its sha is already set
100
+ to a value, the object will just be stored in the our database format,
101
+ in which case the input stream is expected to be in object format ( header + contents ).
102
+ :raise IOError: if data could not be written"""
103
+ raise NotImplementedError("To be implemented in subclass")
104
+
105
+ #} END edit interface
106
+
107
+
108
+ class FileDBBase:
109
+
110
+ """Provides basic facilities to retrieve files of interest, including
111
+ caching facilities to help mapping hexsha's to objects"""
112
+
113
+ def __init__(self, root_path):
114
+ """Initialize this instance to look for its files at the given root path
115
+ All subsequent operations will be relative to this path
116
+ :raise InvalidDBRoot:
117
+ **Note:** The base will not perform any accessablity checking as the base
118
+ might not yet be accessible, but become accessible before the first
119
+ access."""
120
+ super().__init__()
121
+ self._root_path = root_path
122
+
123
+ #{ Interface
124
+ def root_path(self):
125
+ """:return: path at which this db operates"""
126
+ return self._root_path
127
+
128
+ def db_path(self, rela_path):
129
+ """
130
+ :return: the given relative path relative to our database root, allowing
131
+ to pontentially access datafiles"""
132
+ return join(self._root_path, force_text(rela_path))
133
+ #} END interface
134
+
135
+
136
+ class CachingDB:
137
+
138
+ """A database which uses caches to speed-up access"""
139
+
140
+ #{ Interface
141
+ def update_cache(self, force=False):
142
+ """
143
+ Call this method if the underlying data changed to trigger an update
144
+ of the internal caching structures.
145
+
146
+ :param force: if True, the update must be performed. Otherwise the implementation
147
+ may decide not to perform an update if it thinks nothing has changed.
148
+ :return: True if an update was performed as something change indeed"""
149
+
150
+ # END interface
151
+
152
+
153
+ def _databases_recursive(database, output):
154
+ """Fill output list with database from db, in order. Deals with Loose, Packed
155
+ and compound databases."""
156
+ if isinstance(database, CompoundDB):
157
+ dbs = database.databases()
158
+ output.extend(db for db in dbs if not isinstance(db, CompoundDB))
159
+ for cdb in (db for db in dbs if isinstance(db, CompoundDB)):
160
+ _databases_recursive(cdb, output)
161
+ else:
162
+ output.append(database)
163
+ # END handle database type
164
+
165
+
166
+ class CompoundDB(ObjectDBR, LazyMixin, CachingDB):
167
+
168
+ """A database which delegates calls to sub-databases.
169
+
170
+ Databases are stored in the lazy-loaded _dbs attribute.
171
+ Define _set_cache_ to update it with your databases"""
172
+
173
+ def _set_cache_(self, attr):
174
+ if attr == '_dbs':
175
+ self._dbs = list()
176
+ elif attr == '_db_cache':
177
+ self._db_cache = dict()
178
+ else:
179
+ super()._set_cache_(attr)
180
+
181
+ def _db_query(self, sha):
182
+ """:return: database containing the given 20 byte sha
183
+ :raise BadObject:"""
184
+ # most databases use binary representations, prevent converting
185
+ # it every time a database is being queried
186
+ try:
187
+ return self._db_cache[sha]
188
+ except KeyError:
189
+ pass
190
+ # END first level cache
191
+
192
+ for db in self._dbs:
193
+ if db.has_object(sha):
194
+ self._db_cache[sha] = db
195
+ return db
196
+ # END for each database
197
+ raise BadObject(sha)
198
+
199
+ #{ ObjectDBR interface
200
+
201
+ def has_object(self, sha):
202
+ try:
203
+ self._db_query(sha)
204
+ return True
205
+ except BadObject:
206
+ return False
207
+ # END handle exceptions
208
+
209
+ def info(self, sha):
210
+ return self._db_query(sha).info(sha)
211
+
212
+ def stream(self, sha):
213
+ return self._db_query(sha).stream(sha)
214
+
215
+ def size(self):
216
+ """:return: total size of all contained databases"""
217
+ return reduce(lambda x, y: x + y, (db.size() for db in self._dbs), 0)
218
+
219
+ def sha_iter(self):
220
+ return chain(*(db.sha_iter() for db in self._dbs))
221
+
222
+ #} END object DBR Interface
223
+
224
+ #{ Interface
225
+
226
+ def databases(self):
227
+ """:return: tuple of database instances we use for lookups"""
228
+ return tuple(self._dbs)
229
+
230
+ def update_cache(self, force=False):
231
+ # something might have changed, clear everything
232
+ self._db_cache.clear()
233
+ stat = False
234
+ for db in self._dbs:
235
+ if isinstance(db, CachingDB):
236
+ stat |= db.update_cache(force)
237
+ # END if is caching db
238
+ # END for each database to update
239
+ return stat
240
+
241
+ def partial_to_complete_sha_hex(self, partial_hexsha):
242
+ """
243
+ :return: 20 byte binary sha1 from the given less-than-40 byte hexsha (bytes or str)
244
+ :param partial_hexsha: hexsha with less than 40 byte
245
+ :raise AmbiguousObjectName: """
246
+ databases = list()
247
+ _databases_recursive(self, databases)
248
+ partial_hexsha = force_text(partial_hexsha)
249
+ len_partial_hexsha = len(partial_hexsha)
250
+ if len_partial_hexsha % 2 != 0:
251
+ partial_binsha = hex_to_bin(partial_hexsha + "0")
252
+ else:
253
+ partial_binsha = hex_to_bin(partial_hexsha)
254
+ # END assure successful binary conversion
255
+
256
+ candidate = None
257
+ for db in databases:
258
+ full_bin_sha = None
259
+ try:
260
+ if hasattr(db, 'partial_to_complete_sha_hex'):
261
+ full_bin_sha = db.partial_to_complete_sha_hex(partial_hexsha)
262
+ else:
263
+ full_bin_sha = db.partial_to_complete_sha(partial_binsha, len_partial_hexsha)
264
+ # END handle database type
265
+ except BadObject:
266
+ continue
267
+ # END ignore bad objects
268
+ if full_bin_sha:
269
+ if candidate and candidate != full_bin_sha:
270
+ raise AmbiguousObjectName(partial_hexsha)
271
+ candidate = full_bin_sha
272
+ # END handle candidate
273
+ # END for each db
274
+ if not candidate:
275
+ raise BadObject(partial_binsha)
276
+ return candidate
277
+
278
+ #} END interface
lib/python3.12/site-packages/gitdb/db/git.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ from gitdb.db.base import (
6
+ CompoundDB,
7
+ ObjectDBW,
8
+ FileDBBase
9
+ )
10
+
11
+ from gitdb.db.loose import LooseObjectDB
12
+ from gitdb.db.pack import PackedDB
13
+ from gitdb.db.ref import ReferenceDB
14
+
15
+ from gitdb.exc import InvalidDBRoot
16
+
17
+ import os
18
+
19
+ __all__ = ('GitDB', )
20
+
21
+
22
+ class GitDB(FileDBBase, ObjectDBW, CompoundDB):
23
+
24
+ """A git-style object database, which contains all objects in the 'objects'
25
+ subdirectory
26
+
27
+ ``IMPORTANT``: The usage of this implementation is highly discouraged as it fails to release file-handles.
28
+ This can be a problem with long-running processes and/or big repositories.
29
+ """
30
+ # Configuration
31
+ PackDBCls = PackedDB
32
+ LooseDBCls = LooseObjectDB
33
+ ReferenceDBCls = ReferenceDB
34
+
35
+ # Directories
36
+ packs_dir = 'pack'
37
+ loose_dir = ''
38
+ alternates_dir = os.path.join('info', 'alternates')
39
+
40
+ def __init__(self, root_path):
41
+ """Initialize ourselves on a git objects directory"""
42
+ super().__init__(root_path)
43
+
44
+ def _set_cache_(self, attr):
45
+ if attr == '_dbs' or attr == '_loose_db':
46
+ self._dbs = list()
47
+ loose_db = None
48
+ for subpath, dbcls in ((self.packs_dir, self.PackDBCls),
49
+ (self.loose_dir, self.LooseDBCls),
50
+ (self.alternates_dir, self.ReferenceDBCls)):
51
+ path = self.db_path(subpath)
52
+ if os.path.exists(path):
53
+ self._dbs.append(dbcls(path))
54
+ if dbcls is self.LooseDBCls:
55
+ loose_db = self._dbs[-1]
56
+ # END remember loose db
57
+ # END check path exists
58
+ # END for each db type
59
+
60
+ # should have at least one subdb
61
+ if not self._dbs:
62
+ raise InvalidDBRoot(self.root_path())
63
+ # END handle error
64
+
65
+ # we the first one should have the store method
66
+ assert loose_db is not None and hasattr(loose_db, 'store'), "First database needs store functionality"
67
+
68
+ # finally set the value
69
+ self._loose_db = loose_db
70
+ else:
71
+ super()._set_cache_(attr)
72
+ # END handle attrs
73
+
74
+ #{ ObjectDBW interface
75
+
76
+ def store(self, istream):
77
+ return self._loose_db.store(istream)
78
+
79
+ def ostream(self):
80
+ return self._loose_db.ostream()
81
+
82
+ def set_ostream(self, ostream):
83
+ return self._loose_db.set_ostream(ostream)
84
+
85
+ #} END objectdbw interface
lib/python3.12/site-packages/gitdb/db/loose.py ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ from contextlib import suppress
6
+
7
+ from gitdb.db.base import (
8
+ FileDBBase,
9
+ ObjectDBR,
10
+ ObjectDBW
11
+ )
12
+
13
+ from gitdb.exc import (
14
+ BadObject,
15
+ AmbiguousObjectName
16
+ )
17
+
18
+ from gitdb.stream import (
19
+ DecompressMemMapReader,
20
+ FDCompressedSha1Writer,
21
+ FDStream,
22
+ Sha1Writer
23
+ )
24
+
25
+ from gitdb.base import (
26
+ OStream,
27
+ OInfo
28
+ )
29
+
30
+ from gitdb.util import (
31
+ file_contents_ro_filepath,
32
+ ENOENT,
33
+ hex_to_bin,
34
+ bin_to_hex,
35
+ exists,
36
+ chmod,
37
+ isfile,
38
+ remove,
39
+ rename,
40
+ dirname,
41
+ basename,
42
+ join
43
+ )
44
+
45
+ from gitdb.fun import (
46
+ chunk_size,
47
+ loose_object_header_info,
48
+ write_object,
49
+ stream_copy
50
+ )
51
+
52
+ from gitdb.utils.encoding import force_bytes
53
+
54
+ import tempfile
55
+ import os
56
+ import sys
57
+ import time
58
+
59
+
60
+ __all__ = ('LooseObjectDB', )
61
+
62
+
63
+ class LooseObjectDB(FileDBBase, ObjectDBR, ObjectDBW):
64
+
65
+ """A database which operates on loose object files"""
66
+
67
+ # CONFIGURATION
68
+ # chunks in which data will be copied between streams
69
+ stream_chunk_size = chunk_size
70
+
71
+ # On windows we need to keep it writable, otherwise it cannot be removed
72
+ # either
73
+ new_objects_mode = int("444", 8)
74
+ if os.name == 'nt':
75
+ new_objects_mode = int("644", 8)
76
+
77
+ def __init__(self, root_path):
78
+ super().__init__(root_path)
79
+ self._hexsha_to_file = dict()
80
+ # Additional Flags - might be set to 0 after the first failure
81
+ # Depending on the root, this might work for some mounts, for others not, which
82
+ # is why it is per instance
83
+ self._fd_open_flags = getattr(os, 'O_NOATIME', 0)
84
+
85
+ #{ Interface
86
+ def object_path(self, hexsha):
87
+ """
88
+ :return: path at which the object with the given hexsha would be stored,
89
+ relative to the database root"""
90
+ return join(hexsha[:2], hexsha[2:])
91
+
92
+ def readable_db_object_path(self, hexsha):
93
+ """
94
+ :return: readable object path to the object identified by hexsha
95
+ :raise BadObject: If the object file does not exist"""
96
+ with suppress(KeyError):
97
+ return self._hexsha_to_file[hexsha]
98
+ # END ignore cache misses
99
+
100
+ # try filesystem
101
+ path = self.db_path(self.object_path(hexsha))
102
+ if exists(path):
103
+ self._hexsha_to_file[hexsha] = path
104
+ return path
105
+ # END handle cache
106
+ raise BadObject(hexsha)
107
+
108
+ def partial_to_complete_sha_hex(self, partial_hexsha):
109
+ """:return: 20 byte binary sha1 string which matches the given name uniquely
110
+ :param name: hexadecimal partial name (bytes or ascii string)
111
+ :raise AmbiguousObjectName:
112
+ :raise BadObject: """
113
+ candidate = None
114
+ for binsha in self.sha_iter():
115
+ if bin_to_hex(binsha).startswith(force_bytes(partial_hexsha)):
116
+ # it can't ever find the same object twice
117
+ if candidate is not None:
118
+ raise AmbiguousObjectName(partial_hexsha)
119
+ candidate = binsha
120
+ # END for each object
121
+ if candidate is None:
122
+ raise BadObject(partial_hexsha)
123
+ return candidate
124
+
125
+ #} END interface
126
+
127
+ def _map_loose_object(self, sha):
128
+ """
129
+ :return: memory map of that file to allow random read access
130
+ :raise BadObject: if object could not be located"""
131
+ db_path = self.db_path(self.object_path(bin_to_hex(sha)))
132
+ try:
133
+ return file_contents_ro_filepath(db_path, flags=self._fd_open_flags)
134
+ except OSError as e:
135
+ if e.errno != ENOENT:
136
+ # try again without noatime
137
+ try:
138
+ return file_contents_ro_filepath(db_path)
139
+ except OSError as new_e:
140
+ raise BadObject(sha) from new_e
141
+ # didn't work because of our flag, don't try it again
142
+ self._fd_open_flags = 0
143
+ else:
144
+ raise BadObject(sha) from e
145
+ # END handle error
146
+ # END exception handling
147
+
148
+ def set_ostream(self, stream):
149
+ """:raise TypeError: if the stream does not support the Sha1Writer interface"""
150
+ if stream is not None and not isinstance(stream, Sha1Writer):
151
+ raise TypeError("Output stream musst support the %s interface" % Sha1Writer.__name__)
152
+ return super().set_ostream(stream)
153
+
154
+ def info(self, sha):
155
+ m = self._map_loose_object(sha)
156
+ try:
157
+ typ, size = loose_object_header_info(m)
158
+ return OInfo(sha, typ, size)
159
+ finally:
160
+ if hasattr(m, 'close'):
161
+ m.close()
162
+ # END assure release of system resources
163
+
164
+ def stream(self, sha):
165
+ m = self._map_loose_object(sha)
166
+ type, size, stream = DecompressMemMapReader.new(m, close_on_deletion=True)
167
+ return OStream(sha, type, size, stream)
168
+
169
+ def has_object(self, sha):
170
+ try:
171
+ self.readable_db_object_path(bin_to_hex(sha))
172
+ return True
173
+ except BadObject:
174
+ return False
175
+ # END check existence
176
+
177
+ def store(self, istream):
178
+ """note: The sha we produce will be hex by nature"""
179
+ tmp_path = None
180
+ writer = self.ostream()
181
+ if writer is None:
182
+ # open a tmp file to write the data to
183
+ fd, tmp_path = tempfile.mkstemp(prefix='obj', dir=self._root_path)
184
+
185
+ if istream.binsha is None:
186
+ writer = FDCompressedSha1Writer(fd)
187
+ else:
188
+ writer = FDStream(fd)
189
+ # END handle direct stream copies
190
+ # END handle custom writer
191
+
192
+ try:
193
+ try:
194
+ if istream.binsha is not None:
195
+ # copy as much as possible, the actual uncompressed item size might
196
+ # be smaller than the compressed version
197
+ stream_copy(istream.read, writer.write, sys.maxsize, self.stream_chunk_size)
198
+ else:
199
+ # write object with header, we have to make a new one
200
+ write_object(istream.type, istream.size, istream.read, writer.write,
201
+ chunk_size=self.stream_chunk_size)
202
+ # END handle direct stream copies
203
+ finally:
204
+ if tmp_path:
205
+ writer.close()
206
+ # END assure target stream is closed
207
+ except:
208
+ if tmp_path:
209
+ remove(tmp_path)
210
+ raise
211
+ # END assure tmpfile removal on error
212
+
213
+ hexsha = None
214
+ if istream.binsha:
215
+ hexsha = istream.hexsha
216
+ else:
217
+ hexsha = writer.sha(as_hex=True)
218
+ # END handle sha
219
+
220
+ if tmp_path:
221
+ obj_path = self.db_path(self.object_path(hexsha))
222
+ obj_dir = dirname(obj_path)
223
+ os.makedirs(obj_dir, exist_ok=True)
224
+ # END handle destination directory
225
+ # rename onto existing doesn't work on NTFS
226
+ if isfile(obj_path):
227
+ remove(tmp_path)
228
+ else:
229
+ rename(tmp_path, obj_path)
230
+ # end rename only if needed
231
+
232
+ # Ensure rename is actually done and file is stable
233
+ # Retry up to 14 times - exponential wait & retry in ms.
234
+ # The total maximum wait time is 1000ms, which should be vastly enough for the
235
+ # OS to return and commit the file to disk.
236
+ for exp_backoff_ms in [1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 181]:
237
+ with suppress(PermissionError):
238
+ # make sure its readable for all ! It started out as rw-- tmp file
239
+ # but needs to be rwrr
240
+ chmod(obj_path, self.new_objects_mode)
241
+ break
242
+ time.sleep(exp_backoff_ms / 1000.0)
243
+ else:
244
+ raise PermissionError(
245
+ "Impossible to apply `chmod` to file {}".format(obj_path)
246
+ )
247
+
248
+ # END handle dry_run
249
+
250
+ istream.binsha = hex_to_bin(hexsha)
251
+ return istream
252
+
253
+ def sha_iter(self):
254
+ # find all files which look like an object, extract sha from there
255
+ for root, dirs, files in os.walk(self.root_path()):
256
+ root_base = basename(root)
257
+ if len(root_base) != 2:
258
+ continue
259
+
260
+ for f in files:
261
+ if len(f) != 38:
262
+ continue
263
+ yield hex_to_bin(root_base + f)
264
+ # END for each file
265
+ # END for each walk iteration
266
+
267
+ def size(self):
268
+ return len(tuple(self.sha_iter()))
lib/python3.12/site-packages/gitdb/db/mem.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ """Contains the MemoryDatabase implementation"""
6
+ from gitdb.db.loose import LooseObjectDB
7
+ from gitdb.db.base import (
8
+ ObjectDBR,
9
+ ObjectDBW
10
+ )
11
+
12
+ from gitdb.base import (
13
+ OStream,
14
+ IStream,
15
+ )
16
+
17
+ from gitdb.exc import (
18
+ BadObject,
19
+ UnsupportedOperation
20
+ )
21
+
22
+ from gitdb.stream import (
23
+ ZippedStoreShaWriter,
24
+ DecompressMemMapReader,
25
+ )
26
+
27
+ from io import BytesIO
28
+
29
+ __all__ = ("MemoryDB", )
30
+
31
+
32
+ class MemoryDB(ObjectDBR, ObjectDBW):
33
+
34
+ """A memory database stores everything to memory, providing fast IO and object
35
+ retrieval. It should be used to buffer results and obtain SHAs before writing
36
+ it to the actual physical storage, as it allows to query whether object already
37
+ exists in the target storage before introducing actual IO"""
38
+
39
+ def __init__(self):
40
+ super().__init__()
41
+ self._db = LooseObjectDB("path/doesnt/matter")
42
+
43
+ # maps 20 byte shas to their OStream objects
44
+ self._cache = dict()
45
+
46
+ def set_ostream(self, stream):
47
+ raise UnsupportedOperation("MemoryDB's always stream into memory")
48
+
49
+ def store(self, istream):
50
+ zstream = ZippedStoreShaWriter()
51
+ self._db.set_ostream(zstream)
52
+
53
+ istream = self._db.store(istream)
54
+ zstream.close() # close to flush
55
+ zstream.seek(0)
56
+
57
+ # don't provide a size, the stream is written in object format, hence the
58
+ # header needs decompression
59
+ decomp_stream = DecompressMemMapReader(zstream.getvalue(), close_on_deletion=False)
60
+ self._cache[istream.binsha] = OStream(istream.binsha, istream.type, istream.size, decomp_stream)
61
+
62
+ return istream
63
+
64
+ def has_object(self, sha):
65
+ return sha in self._cache
66
+
67
+ def info(self, sha):
68
+ # we always return streams, which are infos as well
69
+ return self.stream(sha)
70
+
71
+ def stream(self, sha):
72
+ try:
73
+ ostream = self._cache[sha]
74
+ # rewind stream for the next one to read
75
+ ostream.stream.seek(0)
76
+ return ostream
77
+ except KeyError as e:
78
+ raise BadObject(sha) from e
79
+ # END exception handling
80
+
81
+ def size(self):
82
+ return len(self._cache)
83
+
84
+ def sha_iter(self):
85
+ return self._cache.keys()
86
+
87
+ #{ Interface
88
+ def stream_copy(self, sha_iter, odb):
89
+ """Copy the streams as identified by sha's yielded by sha_iter into the given odb
90
+ The streams will be copied directly
91
+ **Note:** the object will only be written if it did not exist in the target db
92
+
93
+ :return: amount of streams actually copied into odb. If smaller than the amount
94
+ of input shas, one or more objects did already exist in odb"""
95
+ count = 0
96
+ for sha in sha_iter:
97
+ if odb.has_object(sha):
98
+ continue
99
+ # END check object existence
100
+
101
+ ostream = self.stream(sha)
102
+ # compressed data including header
103
+ sio = BytesIO(ostream.stream.data())
104
+ istream = IStream(ostream.type, ostream.size, sio, sha)
105
+
106
+ odb.store(istream)
107
+ count += 1
108
+ # END for each sha
109
+ return count
110
+ #} END interface
lib/python3.12/site-packages/gitdb/db/pack.py ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ """Module containing a database to deal with packs"""
6
+ from gitdb.db.base import (
7
+ FileDBBase,
8
+ ObjectDBR,
9
+ CachingDB
10
+ )
11
+
12
+ from gitdb.util import LazyMixin
13
+
14
+ from gitdb.exc import (
15
+ BadObject,
16
+ UnsupportedOperation,
17
+ AmbiguousObjectName
18
+ )
19
+
20
+ from gitdb.pack import PackEntity
21
+
22
+ from functools import reduce
23
+
24
+ import os
25
+ import glob
26
+
27
+ __all__ = ('PackedDB', )
28
+
29
+ #{ Utilities
30
+
31
+
32
+ class PackedDB(FileDBBase, ObjectDBR, CachingDB, LazyMixin):
33
+
34
+ """A database operating on a set of object packs"""
35
+
36
+ # sort the priority list every N queries
37
+ # Higher values are better, performance tests don't show this has
38
+ # any effect, but it should have one
39
+ _sort_interval = 500
40
+
41
+ def __init__(self, root_path):
42
+ super().__init__(root_path)
43
+ # list of lists with three items:
44
+ # * hits - number of times the pack was hit with a request
45
+ # * entity - Pack entity instance
46
+ # * sha_to_index - PackIndexFile.sha_to_index method for direct cache query
47
+ # self._entities = list() # lazy loaded list
48
+ self._hit_count = 0 # amount of hits
49
+ self._st_mtime = 0 # last modification data of our root path
50
+
51
+ def _set_cache_(self, attr):
52
+ if attr == '_entities':
53
+ self._entities = list()
54
+ self.update_cache(force=True)
55
+ # END handle entities initialization
56
+
57
+ def _sort_entities(self):
58
+ self._entities.sort(key=lambda l: l[0], reverse=True)
59
+
60
+ def _pack_info(self, sha):
61
+ """:return: tuple(entity, index) for an item at the given sha
62
+ :param sha: 20 or 40 byte sha
63
+ :raise BadObject:
64
+ **Note:** This method is not thread-safe, but may be hit in multi-threaded
65
+ operation. The worst thing that can happen though is a counter that
66
+ was not incremented, or the list being in wrong order. So we safe
67
+ the time for locking here, lets see how that goes"""
68
+ # presort ?
69
+ if self._hit_count % self._sort_interval == 0:
70
+ self._sort_entities()
71
+ # END update sorting
72
+
73
+ for item in self._entities:
74
+ index = item[2](sha)
75
+ if index is not None:
76
+ item[0] += 1 # one hit for you
77
+ self._hit_count += 1 # general hit count
78
+ return (item[1], index)
79
+ # END index found in pack
80
+ # END for each item
81
+
82
+ # no hit, see whether we have to update packs
83
+ # NOTE: considering packs don't change very often, we safe this call
84
+ # and leave it to the super-caller to trigger that
85
+ raise BadObject(sha)
86
+
87
+ #{ Object DB Read
88
+
89
+ def has_object(self, sha):
90
+ try:
91
+ self._pack_info(sha)
92
+ return True
93
+ except BadObject:
94
+ return False
95
+ # END exception handling
96
+
97
+ def info(self, sha):
98
+ entity, index = self._pack_info(sha)
99
+ return entity.info_at_index(index)
100
+
101
+ def stream(self, sha):
102
+ entity, index = self._pack_info(sha)
103
+ return entity.stream_at_index(index)
104
+
105
+ def sha_iter(self):
106
+ for entity in self.entities():
107
+ index = entity.index()
108
+ sha_by_index = index.sha
109
+ for index in range(index.size()):
110
+ yield sha_by_index(index)
111
+ # END for each index
112
+ # END for each entity
113
+
114
+ def size(self):
115
+ sizes = [item[1].index().size() for item in self._entities]
116
+ return reduce(lambda x, y: x + y, sizes, 0)
117
+
118
+ #} END object db read
119
+
120
+ #{ object db write
121
+
122
+ def store(self, istream):
123
+ """Storing individual objects is not feasible as a pack is designed to
124
+ hold multiple objects. Writing or rewriting packs for single objects is
125
+ inefficient"""
126
+ raise UnsupportedOperation()
127
+
128
+ #} END object db write
129
+
130
+ #{ Interface
131
+
132
+ def update_cache(self, force=False):
133
+ """
134
+ Update our cache with the actually existing packs on disk. Add new ones,
135
+ and remove deleted ones. We keep the unchanged ones
136
+
137
+ :param force: If True, the cache will be updated even though the directory
138
+ does not appear to have changed according to its modification timestamp.
139
+ :return: True if the packs have been updated so there is new information,
140
+ False if there was no change to the pack database"""
141
+ stat = os.stat(self.root_path())
142
+ if not force and stat.st_mtime <= self._st_mtime:
143
+ return False
144
+ # END abort early on no change
145
+ self._st_mtime = stat.st_mtime
146
+
147
+ # packs are supposed to be prefixed with pack- by git-convention
148
+ # get all pack files, figure out what changed
149
+ pack_files = set(glob.glob(os.path.join(self.root_path(), "pack-*.pack")))
150
+ our_pack_files = {item[1].pack().path() for item in self._entities}
151
+
152
+ # new packs
153
+ for pack_file in (pack_files - our_pack_files):
154
+ # init the hit-counter/priority with the size, a good measure for hit-
155
+ # probability. Its implemented so that only 12 bytes will be read
156
+ entity = PackEntity(pack_file)
157
+ self._entities.append([entity.pack().size(), entity, entity.index().sha_to_index])
158
+ # END for each new packfile
159
+
160
+ # removed packs
161
+ for pack_file in (our_pack_files - pack_files):
162
+ del_index = -1
163
+ for i, item in enumerate(self._entities):
164
+ if item[1].pack().path() == pack_file:
165
+ del_index = i
166
+ break
167
+ # END found index
168
+ # END for each entity
169
+ assert del_index != -1
170
+ del(self._entities[del_index])
171
+ # END for each removed pack
172
+
173
+ # reinitialize prioritiess
174
+ self._sort_entities()
175
+ return True
176
+
177
+ def entities(self):
178
+ """:return: list of pack entities operated upon by this database"""
179
+ return [item[1] for item in self._entities]
180
+
181
+ def partial_to_complete_sha(self, partial_binsha, canonical_length):
182
+ """:return: 20 byte sha as inferred by the given partial binary sha
183
+ :param partial_binsha: binary sha with less than 20 bytes
184
+ :param canonical_length: length of the corresponding canonical representation.
185
+ It is required as binary sha's cannot display whether the original hex sha
186
+ had an odd or even number of characters
187
+ :raise AmbiguousObjectName:
188
+ :raise BadObject: """
189
+ candidate = None
190
+ for item in self._entities:
191
+ item_index = item[1].index().partial_sha_to_index(partial_binsha, canonical_length)
192
+ if item_index is not None:
193
+ sha = item[1].index().sha(item_index)
194
+ if candidate and candidate != sha:
195
+ raise AmbiguousObjectName(partial_binsha)
196
+ candidate = sha
197
+ # END handle full sha could be found
198
+ # END for each entity
199
+
200
+ if candidate:
201
+ return candidate
202
+
203
+ # still not found ?
204
+ raise BadObject(partial_binsha)
205
+
206
+ #} END interface
lib/python3.12/site-packages/gitdb/db/ref.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ import codecs
6
+ from gitdb.db.base import (
7
+ CompoundDB,
8
+ )
9
+
10
+ __all__ = ('ReferenceDB', )
11
+
12
+
13
+ class ReferenceDB(CompoundDB):
14
+
15
+ """A database consisting of database referred to in a file"""
16
+
17
+ # Configuration
18
+ # Specifies the object database to use for the paths found in the alternates
19
+ # file. If None, it defaults to the GitDB
20
+ ObjectDBCls = None
21
+
22
+ def __init__(self, ref_file):
23
+ super().__init__()
24
+ self._ref_file = ref_file
25
+
26
+ def _set_cache_(self, attr):
27
+ if attr == '_dbs':
28
+ self._dbs = list()
29
+ self._update_dbs_from_ref_file()
30
+ else:
31
+ super()._set_cache_(attr)
32
+ # END handle attrs
33
+
34
+ def _update_dbs_from_ref_file(self):
35
+ dbcls = self.ObjectDBCls
36
+ if dbcls is None:
37
+ # late import
38
+ from gitdb.db.git import GitDB
39
+ dbcls = GitDB
40
+ # END get db type
41
+
42
+ # try to get as many as possible, don't fail if some are unavailable
43
+ ref_paths = list()
44
+ try:
45
+ with codecs.open(self._ref_file, 'r', encoding="utf-8") as f:
46
+ ref_paths = [l.strip() for l in f]
47
+ except OSError:
48
+ pass
49
+ # END handle alternates
50
+
51
+ ref_paths_set = set(ref_paths)
52
+ cur_ref_paths_set = {db.root_path() for db in self._dbs}
53
+
54
+ # remove existing
55
+ for path in (cur_ref_paths_set - ref_paths_set):
56
+ for i, db in enumerate(self._dbs[:]):
57
+ if db.root_path() == path:
58
+ del(self._dbs[i])
59
+ continue
60
+ # END del matching db
61
+ # END for each path to remove
62
+
63
+ # add new
64
+ # sort them to maintain order
65
+ added_paths = sorted(ref_paths_set - cur_ref_paths_set, key=lambda p: ref_paths.index(p))
66
+ for path in added_paths:
67
+ try:
68
+ db = dbcls(path)
69
+ # force an update to verify path
70
+ if isinstance(db, CompoundDB):
71
+ db.databases()
72
+ # END verification
73
+ self._dbs.append(db)
74
+ except Exception:
75
+ # ignore invalid paths or issues
76
+ pass
77
+ # END for each path to add
78
+
79
+ def update_cache(self, force=False):
80
+ # re-read alternates and update databases
81
+ self._update_dbs_from_ref_file()
82
+ return super().update_cache(force)
lib/python3.12/site-packages/gitdb/exc.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ """Module with common exceptions"""
6
+ from gitdb.util import to_hex_sha
7
+
8
+ __all__ = [
9
+ 'AmbiguousObjectName',
10
+ 'BadName',
11
+ 'BadObject',
12
+ 'BadObjectType',
13
+ 'InvalidDBRoot',
14
+ 'ODBError',
15
+ 'ParseError',
16
+ 'UnsupportedOperation',
17
+ 'to_hex_sha',
18
+ ]
19
+
20
+ class ODBError(Exception):
21
+ """All errors thrown by the object database"""
22
+
23
+
24
+ class InvalidDBRoot(ODBError):
25
+ """Thrown if an object database cannot be initialized at the given path"""
26
+
27
+
28
+ class BadObject(ODBError):
29
+ """The object with the given SHA does not exist. Instantiate with the
30
+ failed sha"""
31
+
32
+ def __str__(self):
33
+ return "BadObject: %s" % to_hex_sha(self.args[0])
34
+
35
+
36
+ class BadName(ODBError):
37
+ """A name provided to rev_parse wasn't understood"""
38
+
39
+ def __str__(self):
40
+ return "Ref '%s' did not resolve to an object" % self.args[0]
41
+
42
+
43
+ class ParseError(ODBError):
44
+ """Thrown if the parsing of a file failed due to an invalid format"""
45
+
46
+
47
+ class AmbiguousObjectName(ODBError):
48
+ """Thrown if a possibly shortened name does not uniquely represent a single object
49
+ in the database"""
50
+
51
+
52
+ class BadObjectType(ODBError):
53
+ """The object had an unsupported type"""
54
+
55
+
56
+ class UnsupportedOperation(ODBError):
57
+ """Thrown if the given operation cannot be supported by the object database"""
lib/python3.12/site-packages/gitdb/fun.py ADDED
@@ -0,0 +1,704 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ """Contains basic c-functions which usually contain performance critical code
6
+ Keeping this code separate from the beginning makes it easier to out-source
7
+ it into c later, if required"""
8
+
9
+ import zlib
10
+ from gitdb.util import byte_ord
11
+ decompressobj = zlib.decompressobj
12
+
13
+ import mmap
14
+ from itertools import islice
15
+ from functools import reduce
16
+
17
+ from gitdb.const import NULL_BYTE, BYTE_SPACE
18
+ from gitdb.utils.encoding import force_text
19
+ from gitdb.typ import (
20
+ str_blob_type,
21
+ str_commit_type,
22
+ str_tree_type,
23
+ str_tag_type,
24
+ )
25
+
26
+ from io import StringIO
27
+
28
+ # INVARIANTS
29
+ OFS_DELTA = 6
30
+ REF_DELTA = 7
31
+ delta_types = (OFS_DELTA, REF_DELTA)
32
+
33
+ type_id_to_type_map = {
34
+ 0: b'', # EXT 1
35
+ 1: str_commit_type,
36
+ 2: str_tree_type,
37
+ 3: str_blob_type,
38
+ 4: str_tag_type,
39
+ 5: b'', # EXT 2
40
+ OFS_DELTA: "OFS_DELTA", # OFFSET DELTA
41
+ REF_DELTA: "REF_DELTA" # REFERENCE DELTA
42
+ }
43
+
44
+ type_to_type_id_map = {
45
+ str_commit_type: 1,
46
+ str_tree_type: 2,
47
+ str_blob_type: 3,
48
+ str_tag_type: 4,
49
+ "OFS_DELTA": OFS_DELTA,
50
+ "REF_DELTA": REF_DELTA,
51
+ }
52
+
53
+ # used when dealing with larger streams
54
+ chunk_size = 1000 * mmap.PAGESIZE
55
+
56
+ __all__ = ('is_loose_object', 'loose_object_header_info', 'msb_size', 'pack_object_header_info',
57
+ 'write_object', 'loose_object_header', 'stream_copy', 'apply_delta_data',
58
+ 'is_equal_canonical_sha', 'connect_deltas', 'DeltaChunkList', 'create_pack_object_header')
59
+
60
+
61
+ #{ Structures
62
+
63
+ def _set_delta_rbound(d, size):
64
+ """Truncate the given delta to the given size
65
+ :param size: size relative to our target offset, may not be 0, must be smaller or equal
66
+ to our size
67
+ :return: d"""
68
+ d.ts = size
69
+
70
+ # NOTE: data is truncated automatically when applying the delta
71
+ # MUST NOT DO THIS HERE
72
+ return d
73
+
74
+
75
+ def _move_delta_lbound(d, bytes):
76
+ """Move the delta by the given amount of bytes, reducing its size so that its
77
+ right bound stays static
78
+ :param bytes: amount of bytes to move, must be smaller than delta size
79
+ :return: d"""
80
+ if bytes == 0:
81
+ return
82
+
83
+ d.to += bytes
84
+ d.so += bytes
85
+ d.ts -= bytes
86
+ if d.data is not None:
87
+ d.data = d.data[bytes:]
88
+ # END handle data
89
+
90
+ return d
91
+
92
+
93
+ def delta_duplicate(src):
94
+ return DeltaChunk(src.to, src.ts, src.so, src.data)
95
+
96
+
97
+ def delta_chunk_apply(dc, bbuf, write):
98
+ """Apply own data to the target buffer
99
+ :param bbuf: buffer providing source bytes for copy operations
100
+ :param write: write method to call with data to write"""
101
+ if dc.data is None:
102
+ # COPY DATA FROM SOURCE
103
+ write(bbuf[dc.so:dc.so + dc.ts])
104
+ else:
105
+ # APPEND DATA
106
+ # what's faster: if + 4 function calls or just a write with a slice ?
107
+ # Considering data can be larger than 127 bytes now, it should be worth it
108
+ if dc.ts < len(dc.data):
109
+ write(dc.data[:dc.ts])
110
+ else:
111
+ write(dc.data)
112
+ # END handle truncation
113
+ # END handle chunk mode
114
+
115
+
116
+ class DeltaChunk:
117
+
118
+ """Represents a piece of a delta, it can either add new data, or copy existing
119
+ one from a source buffer"""
120
+ __slots__ = (
121
+ 'to', # start offset in the target buffer in bytes
122
+ 'ts', # size of this chunk in the target buffer in bytes
123
+ 'so', # start offset in the source buffer in bytes or None
124
+ 'data', # chunk of bytes to be added to the target buffer,
125
+ # DeltaChunkList to use as base, or None
126
+ )
127
+
128
+ def __init__(self, to, ts, so, data):
129
+ self.to = to
130
+ self.ts = ts
131
+ self.so = so
132
+ self.data = data
133
+
134
+ def __repr__(self):
135
+ return "DeltaChunk(%i, %i, %s, %s)" % (self.to, self.ts, self.so, self.data or "")
136
+
137
+ #{ Interface
138
+
139
+ def rbound(self):
140
+ return self.to + self.ts
141
+
142
+ def has_data(self):
143
+ """:return: True if the instance has data to add to the target stream"""
144
+ return self.data is not None
145
+
146
+ #} END interface
147
+
148
+
149
+ def _closest_index(dcl, absofs):
150
+ """:return: index at which the given absofs should be inserted. The index points
151
+ to the DeltaChunk with a target buffer absofs that equals or is greater than
152
+ absofs.
153
+ **Note:** global method for performance only, it belongs to DeltaChunkList"""
154
+ lo = 0
155
+ hi = len(dcl)
156
+ while lo < hi:
157
+ mid = (lo + hi) / 2
158
+ dc = dcl[mid]
159
+ if dc.to > absofs:
160
+ hi = mid
161
+ elif dc.rbound() > absofs or dc.to == absofs:
162
+ return mid
163
+ else:
164
+ lo = mid + 1
165
+ # END handle bound
166
+ # END for each delta absofs
167
+ return len(dcl) - 1
168
+
169
+
170
+ def delta_list_apply(dcl, bbuf, write):
171
+ """Apply the chain's changes and write the final result using the passed
172
+ write function.
173
+ :param bbuf: base buffer containing the base of all deltas contained in this
174
+ list. It will only be used if the chunk in question does not have a base
175
+ chain.
176
+ :param write: function taking a string of bytes to write to the output"""
177
+ for dc in dcl:
178
+ delta_chunk_apply(dc, bbuf, write)
179
+ # END for each dc
180
+
181
+
182
+ def delta_list_slice(dcl, absofs, size, ndcl):
183
+ """:return: Subsection of this list at the given absolute offset, with the given
184
+ size in bytes.
185
+ :return: None"""
186
+ cdi = _closest_index(dcl, absofs) # delta start index
187
+ cd = dcl[cdi]
188
+ slen = len(dcl)
189
+ lappend = ndcl.append
190
+
191
+ if cd.to != absofs:
192
+ tcd = DeltaChunk(cd.to, cd.ts, cd.so, cd.data)
193
+ _move_delta_lbound(tcd, absofs - cd.to)
194
+ tcd.ts = min(tcd.ts, size)
195
+ lappend(tcd)
196
+ size -= tcd.ts
197
+ cdi += 1
198
+ # END lbound overlap handling
199
+
200
+ while cdi < slen and size:
201
+ # are we larger than the current block
202
+ cd = dcl[cdi]
203
+ if cd.ts <= size:
204
+ lappend(DeltaChunk(cd.to, cd.ts, cd.so, cd.data))
205
+ size -= cd.ts
206
+ else:
207
+ tcd = DeltaChunk(cd.to, cd.ts, cd.so, cd.data)
208
+ tcd.ts = size
209
+ lappend(tcd)
210
+ size -= tcd.ts
211
+ break
212
+ # END hadle size
213
+ cdi += 1
214
+ # END for each chunk
215
+
216
+
217
+ class DeltaChunkList(list):
218
+
219
+ """List with special functionality to deal with DeltaChunks.
220
+ There are two types of lists we represent. The one was created bottom-up, working
221
+ towards the latest delta, the other kind was created top-down, working from the
222
+ latest delta down to the earliest ancestor. This attribute is queryable
223
+ after all processing with is_reversed."""
224
+
225
+ __slots__ = tuple()
226
+
227
+ def rbound(self):
228
+ """:return: rightmost extend in bytes, absolute"""
229
+ if len(self) == 0:
230
+ return 0
231
+ return self[-1].rbound()
232
+
233
+ def lbound(self):
234
+ """:return: leftmost byte at which this chunklist starts"""
235
+ if len(self) == 0:
236
+ return 0
237
+ return self[0].to
238
+
239
+ def size(self):
240
+ """:return: size of bytes as measured by our delta chunks"""
241
+ return self.rbound() - self.lbound()
242
+
243
+ def apply(self, bbuf, write):
244
+ """Only used by public clients, internally we only use the global routines
245
+ for performance"""
246
+ return delta_list_apply(self, bbuf, write)
247
+
248
+ def compress(self):
249
+ """Alter the list to reduce the amount of nodes. Currently we concatenate
250
+ add-chunks
251
+ :return: self"""
252
+ slen = len(self)
253
+ if slen < 2:
254
+ return self
255
+ i = 0
256
+
257
+ first_data_index = None
258
+ while i < slen:
259
+ dc = self[i]
260
+ i += 1
261
+ if dc.data is None:
262
+ if first_data_index is not None and i - 2 - first_data_index > 1:
263
+ # if first_data_index is not None:
264
+ nd = StringIO() # new data
265
+ so = self[first_data_index].to # start offset in target buffer
266
+ for x in range(first_data_index, i - 1):
267
+ xdc = self[x]
268
+ nd.write(xdc.data[:xdc.ts])
269
+ # END collect data
270
+
271
+ del(self[first_data_index:i - 1])
272
+ buf = nd.getvalue()
273
+ self.insert(first_data_index, DeltaChunk(so, len(buf), 0, buf))
274
+
275
+ slen = len(self)
276
+ i = first_data_index + 1
277
+
278
+ # END concatenate data
279
+ first_data_index = None
280
+ continue
281
+ # END skip non-data chunks
282
+
283
+ if first_data_index is None:
284
+ first_data_index = i - 1
285
+ # END iterate list
286
+
287
+ # if slen_orig != len(self):
288
+ # print "INFO: Reduced delta list len to %f %% of former size" % ((float(len(self)) / slen_orig) * 100)
289
+ return self
290
+
291
+ def check_integrity(self, target_size=-1):
292
+ """Verify the list has non-overlapping chunks only, and the total size matches
293
+ target_size
294
+ :param target_size: if not -1, the total size of the chain must be target_size
295
+ :raise AssertionError: if the size doesn't match"""
296
+ if target_size > -1:
297
+ assert self[-1].rbound() == target_size
298
+ assert reduce(lambda x, y: x + y, (d.ts for d in self), 0) == target_size
299
+ # END target size verification
300
+
301
+ if len(self) < 2:
302
+ return
303
+
304
+ # check data
305
+ for dc in self:
306
+ assert dc.ts > 0
307
+ if dc.has_data():
308
+ assert len(dc.data) >= dc.ts
309
+ # END for each dc
310
+
311
+ left = islice(self, 0, len(self) - 1)
312
+ right = iter(self)
313
+ right.next()
314
+ # this is very pythonic - we might have just use index based access here,
315
+ # but this could actually be faster
316
+ for lft, rgt in zip(left, right):
317
+ assert lft.rbound() == rgt.to
318
+ assert lft.to + lft.ts == rgt.to
319
+ # END for each pair
320
+
321
+
322
+ class TopdownDeltaChunkList(DeltaChunkList):
323
+
324
+ """Represents a list which is generated by feeding its ancestor streams one by
325
+ one"""
326
+ __slots__ = tuple()
327
+
328
+ def connect_with_next_base(self, bdcl):
329
+ """Connect this chain with the next level of our base delta chunklist.
330
+ The goal in this game is to mark as many of our chunks rigid, hence they
331
+ cannot be changed by any of the upcoming bases anymore. Once all our
332
+ chunks are marked like that, we can stop all processing
333
+ :param bdcl: data chunk list being one of our bases. They must be fed in
334
+ consecutively and in order, towards the earliest ancestor delta
335
+ :return: True if processing was done. Use it to abort processing of
336
+ remaining streams if False is returned"""
337
+ nfc = 0 # number of frozen chunks
338
+ dci = 0 # delta chunk index
339
+ slen = len(self) # len of self
340
+ ccl = list() # temporary list
341
+ while dci < slen:
342
+ dc = self[dci]
343
+ dci += 1
344
+
345
+ # all add-chunks which are already topmost don't need additional processing
346
+ if dc.data is not None:
347
+ nfc += 1
348
+ continue
349
+ # END skip add chunks
350
+
351
+ # copy chunks
352
+ # integrate the portion of the base list into ourselves. Lists
353
+ # dont support efficient insertion ( just one at a time ), but for now
354
+ # we live with it. Internally, its all just a 32/64bit pointer, and
355
+ # the portions of moved memory should be smallish. Maybe we just rebuild
356
+ # ourselves in order to reduce the amount of insertions ...
357
+ del(ccl[:])
358
+ delta_list_slice(bdcl, dc.so, dc.ts, ccl)
359
+
360
+ # move the target bounds into place to match with our chunk
361
+ ofs = dc.to - dc.so
362
+ for cdc in ccl:
363
+ cdc.to += ofs
364
+ # END update target bounds
365
+
366
+ if len(ccl) == 1:
367
+ self[dci - 1] = ccl[0]
368
+ else:
369
+ # maybe try to compute the expenses here, and pick the right algorithm
370
+ # It would normally be faster than copying everything physically though
371
+ # TODO: Use a deque here, and decide by the index whether to extend
372
+ # or extend left !
373
+ post_dci = self[dci:]
374
+ del(self[dci - 1:]) # include deletion of dc
375
+ self.extend(ccl)
376
+ self.extend(post_dci)
377
+
378
+ slen = len(self)
379
+ dci += len(ccl) - 1 # deleted dc, added rest
380
+
381
+ # END handle chunk replacement
382
+ # END for each chunk
383
+
384
+ if nfc == slen:
385
+ return False
386
+ # END handle completeness
387
+ return True
388
+
389
+
390
+ #} END structures
391
+
392
+ #{ Routines
393
+
394
+ def is_loose_object(m):
395
+ """
396
+ :return: True the file contained in memory map m appears to be a loose object.
397
+ Only the first two bytes are needed"""
398
+ b0, b1 = map(ord, m[:2])
399
+ word = (b0 << 8) + b1
400
+ return b0 == 0x78 and (word % 31) == 0
401
+
402
+
403
+ def loose_object_header_info(m):
404
+ """
405
+ :return: tuple(type_string, uncompressed_size_in_bytes) the type string of the
406
+ object as well as its uncompressed size in bytes.
407
+ :param m: memory map from which to read the compressed object data"""
408
+ decompress_size = 8192 # is used in cgit as well
409
+ hdr = decompressobj().decompress(m, decompress_size)
410
+ type_name, size = hdr[:hdr.find(NULL_BYTE)].split(BYTE_SPACE)
411
+
412
+ return type_name, int(size)
413
+
414
+
415
+ def pack_object_header_info(data):
416
+ """
417
+ :return: tuple(type_id, uncompressed_size_in_bytes, byte_offset)
418
+ The type_id should be interpreted according to the ``type_id_to_type_map`` map
419
+ The byte-offset specifies the start of the actual zlib compressed datastream
420
+ :param m: random-access memory, like a string or memory map"""
421
+ c = byte_ord(data[0]) # first byte
422
+ i = 1 # next char to read
423
+ type_id = (c >> 4) & 7 # numeric type
424
+ size = c & 15 # starting size
425
+ s = 4 # starting bit-shift size
426
+ while c & 0x80:
427
+ c = byte_ord(data[i])
428
+ i += 1
429
+ size += (c & 0x7f) << s
430
+ s += 7
431
+ # END character loop
432
+ # end performance at expense of maintenance ...
433
+ return (type_id, size, i)
434
+
435
+
436
+ def create_pack_object_header(obj_type, obj_size):
437
+ """
438
+ :return: string defining the pack header comprised of the object type
439
+ and its incompressed size in bytes
440
+
441
+ :param obj_type: pack type_id of the object
442
+ :param obj_size: uncompressed size in bytes of the following object stream"""
443
+ c = 0 # 1 byte
444
+ hdr = bytearray() # output string
445
+
446
+ c = (obj_type << 4) | (obj_size & 0xf)
447
+ obj_size >>= 4
448
+ while obj_size:
449
+ hdr.append(c | 0x80)
450
+ c = obj_size & 0x7f
451
+ obj_size >>= 7
452
+ # END until size is consumed
453
+ hdr.append(c)
454
+ # end handle interpreter
455
+ return hdr
456
+
457
+
458
+ def msb_size(data, offset=0):
459
+ """
460
+ :return: tuple(read_bytes, size) read the msb size from the given random
461
+ access data starting at the given byte offset"""
462
+ size = 0
463
+ i = 0
464
+ l = len(data)
465
+ hit_msb = False
466
+ while i < l:
467
+ c = data[i + offset]
468
+ size |= (c & 0x7f) << i * 7
469
+ i += 1
470
+ if not c & 0x80:
471
+ hit_msb = True
472
+ break
473
+ # END check msb bit
474
+ # END while in range
475
+ # end performance ...
476
+ if not hit_msb:
477
+ raise AssertionError("Could not find terminating MSB byte in data stream")
478
+ return i + offset, size
479
+
480
+
481
+ def loose_object_header(type, size):
482
+ """
483
+ :return: bytes representing the loose object header, which is immediately
484
+ followed by the content stream of size 'size'"""
485
+ return ('%s %i\0' % (force_text(type), size)).encode('ascii')
486
+
487
+
488
+ def write_object(type, size, read, write, chunk_size=chunk_size):
489
+ """
490
+ Write the object as identified by type, size and source_stream into the
491
+ target_stream
492
+
493
+ :param type: type string of the object
494
+ :param size: amount of bytes to write from source_stream
495
+ :param read: read method of a stream providing the content data
496
+ :param write: write method of the output stream
497
+ :param close_target_stream: if True, the target stream will be closed when
498
+ the routine exits, even if an error is thrown
499
+ :return: The actual amount of bytes written to stream, which includes the header and a trailing newline"""
500
+ tbw = 0 # total num bytes written
501
+
502
+ # WRITE HEADER: type SP size NULL
503
+ tbw += write(loose_object_header(type, size))
504
+ tbw += stream_copy(read, write, size, chunk_size)
505
+
506
+ return tbw
507
+
508
+
509
+ def stream_copy(read, write, size, chunk_size):
510
+ """
511
+ Copy a stream up to size bytes using the provided read and write methods,
512
+ in chunks of chunk_size
513
+
514
+ **Note:** its much like stream_copy utility, but operates just using methods"""
515
+ dbw = 0 # num data bytes written
516
+
517
+ # WRITE ALL DATA UP TO SIZE
518
+ while True:
519
+ cs = min(chunk_size, size - dbw)
520
+ # NOTE: not all write methods return the amount of written bytes, like
521
+ # mmap.write. Its bad, but we just deal with it ... perhaps its not
522
+ # even less efficient
523
+ # data_len = write(read(cs))
524
+ # dbw += data_len
525
+ data = read(cs)
526
+ data_len = len(data)
527
+ dbw += data_len
528
+ write(data)
529
+ if data_len < cs or dbw == size:
530
+ break
531
+ # END check for stream end
532
+ # END duplicate data
533
+ return dbw
534
+
535
+
536
+ def connect_deltas(dstreams):
537
+ """
538
+ Read the condensed delta chunk information from dstream and merge its information
539
+ into a list of existing delta chunks
540
+
541
+ :param dstreams: iterable of delta stream objects, the delta to be applied last
542
+ comes first, then all its ancestors in order
543
+ :return: DeltaChunkList, containing all operations to apply"""
544
+ tdcl = None # topmost dcl
545
+
546
+ dcl = tdcl = TopdownDeltaChunkList()
547
+ for dsi, ds in enumerate(dstreams):
548
+ # print "Stream", dsi
549
+ db = ds.read()
550
+ delta_buf_size = ds.size
551
+
552
+ # read header
553
+ i, base_size = msb_size(db)
554
+ i, target_size = msb_size(db, i)
555
+
556
+ # interpret opcodes
557
+ tbw = 0 # amount of target bytes written
558
+ while i < delta_buf_size:
559
+ c = ord(db[i])
560
+ i += 1
561
+ if c & 0x80:
562
+ cp_off, cp_size = 0, 0
563
+ if (c & 0x01):
564
+ cp_off = ord(db[i])
565
+ i += 1
566
+ if (c & 0x02):
567
+ cp_off |= (ord(db[i]) << 8)
568
+ i += 1
569
+ if (c & 0x04):
570
+ cp_off |= (ord(db[i]) << 16)
571
+ i += 1
572
+ if (c & 0x08):
573
+ cp_off |= (ord(db[i]) << 24)
574
+ i += 1
575
+ if (c & 0x10):
576
+ cp_size = ord(db[i])
577
+ i += 1
578
+ if (c & 0x20):
579
+ cp_size |= (ord(db[i]) << 8)
580
+ i += 1
581
+ if (c & 0x40):
582
+ cp_size |= (ord(db[i]) << 16)
583
+ i += 1
584
+
585
+ if not cp_size:
586
+ cp_size = 0x10000
587
+
588
+ rbound = cp_off + cp_size
589
+ if (rbound < cp_size or
590
+ rbound > base_size):
591
+ break
592
+
593
+ dcl.append(DeltaChunk(tbw, cp_size, cp_off, None))
594
+ tbw += cp_size
595
+ elif c:
596
+ # NOTE: in C, the data chunks should probably be concatenated here.
597
+ # In python, we do it as a post-process
598
+ dcl.append(DeltaChunk(tbw, c, 0, db[i:i + c]))
599
+ i += c
600
+ tbw += c
601
+ else:
602
+ raise ValueError("unexpected delta opcode 0")
603
+ # END handle command byte
604
+ # END while processing delta data
605
+
606
+ dcl.compress()
607
+
608
+ # merge the lists !
609
+ if dsi > 0:
610
+ if not tdcl.connect_with_next_base(dcl):
611
+ break
612
+ # END handle merge
613
+
614
+ # prepare next base
615
+ dcl = DeltaChunkList()
616
+ # END for each delta stream
617
+
618
+ return tdcl
619
+
620
+
621
+ def apply_delta_data(src_buf, src_buf_size, delta_buf, delta_buf_size, write):
622
+ """
623
+ Apply data from a delta buffer using a source buffer to the target file
624
+
625
+ :param src_buf: random access data from which the delta was created
626
+ :param src_buf_size: size of the source buffer in bytes
627
+ :param delta_buf_size: size for the delta buffer in bytes
628
+ :param delta_buf: random access delta data
629
+ :param write: write method taking a chunk of bytes
630
+
631
+ **Note:** transcribed to python from the similar routine in patch-delta.c"""
632
+ i = 0
633
+ db = delta_buf
634
+ while i < delta_buf_size:
635
+ c = db[i]
636
+ i += 1
637
+ if c & 0x80:
638
+ cp_off, cp_size = 0, 0
639
+ if (c & 0x01):
640
+ cp_off = db[i]
641
+ i += 1
642
+ if (c & 0x02):
643
+ cp_off |= (db[i] << 8)
644
+ i += 1
645
+ if (c & 0x04):
646
+ cp_off |= (db[i] << 16)
647
+ i += 1
648
+ if (c & 0x08):
649
+ cp_off |= (db[i] << 24)
650
+ i += 1
651
+ if (c & 0x10):
652
+ cp_size = db[i]
653
+ i += 1
654
+ if (c & 0x20):
655
+ cp_size |= (db[i] << 8)
656
+ i += 1
657
+ if (c & 0x40):
658
+ cp_size |= (db[i] << 16)
659
+ i += 1
660
+
661
+ if not cp_size:
662
+ cp_size = 0x10000
663
+
664
+ rbound = cp_off + cp_size
665
+ if (rbound < cp_size or
666
+ rbound > src_buf_size):
667
+ break
668
+ write(src_buf[cp_off:cp_off + cp_size])
669
+ elif c:
670
+ write(db[i:i + c])
671
+ i += c
672
+ else:
673
+ raise ValueError("unexpected delta opcode 0")
674
+ # END handle command byte
675
+ # END while processing delta data
676
+
677
+ # yes, lets use the exact same error message that git uses :)
678
+ assert i == delta_buf_size, "delta replay has gone wild"
679
+
680
+
681
+ def is_equal_canonical_sha(canonical_length, match, sha1):
682
+ """
683
+ :return: True if the given lhs and rhs 20 byte binary shas
684
+ The comparison will take the canonical_length of the match sha into account,
685
+ hence the comparison will only use the last 4 bytes for uneven canonical representations
686
+ :param match: less than 20 byte sha
687
+ :param sha1: 20 byte sha"""
688
+ binary_length = canonical_length // 2
689
+ if match[:binary_length] != sha1[:binary_length]:
690
+ return False
691
+
692
+ if canonical_length - binary_length and \
693
+ (byte_ord(match[-1]) ^ byte_ord(sha1[len(match) - 1])) & 0xf0:
694
+ return False
695
+ # END handle uneven canonnical length
696
+ return True
697
+
698
+ #} END routines
699
+
700
+
701
+ try:
702
+ from gitdb_speedups._perf import connect_deltas
703
+ except ImportError:
704
+ pass
lib/python3.12/site-packages/gitdb/pack.py ADDED
@@ -0,0 +1,1031 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ """Contains PackIndexFile and PackFile implementations"""
6
+ import zlib
7
+
8
+ from gitdb.exc import (
9
+ BadObject,
10
+ AmbiguousObjectName,
11
+ UnsupportedOperation,
12
+ ParseError
13
+ )
14
+
15
+ from gitdb.util import (
16
+ mman,
17
+ LazyMixin,
18
+ unpack_from,
19
+ bin_to_hex,
20
+ byte_ord,
21
+ )
22
+
23
+ from gitdb.fun import (
24
+ create_pack_object_header,
25
+ pack_object_header_info,
26
+ is_equal_canonical_sha,
27
+ type_id_to_type_map,
28
+ write_object,
29
+ stream_copy,
30
+ chunk_size,
31
+ delta_types,
32
+ OFS_DELTA,
33
+ REF_DELTA,
34
+ msb_size
35
+ )
36
+
37
+ try:
38
+ from gitdb_speedups._perf import PackIndexFile_sha_to_index
39
+ except ImportError:
40
+ pass
41
+ # END try c module
42
+
43
+ from gitdb.base import ( # Amazing !
44
+ OInfo,
45
+ OStream,
46
+ OPackInfo,
47
+ OPackStream,
48
+ ODeltaStream,
49
+ ODeltaPackInfo,
50
+ ODeltaPackStream,
51
+ )
52
+
53
+ from gitdb.stream import (
54
+ DecompressMemMapReader,
55
+ DeltaApplyReader,
56
+ Sha1Writer,
57
+ NullStream,
58
+ FlexibleSha1Writer
59
+ )
60
+
61
+ from struct import pack
62
+ from binascii import crc32
63
+
64
+ from gitdb.const import NULL_BYTE
65
+
66
+ import tempfile
67
+ import array
68
+ import os
69
+ import sys
70
+
71
+ __all__ = ('PackIndexFile', 'PackFile', 'PackEntity')
72
+
73
+
74
+ #{ Utilities
75
+
76
+ def pack_object_at(cursor, offset, as_stream):
77
+ """
78
+ :return: Tuple(abs_data_offset, PackInfo|PackStream)
79
+ an object of the correct type according to the type_id of the object.
80
+ If as_stream is True, the object will contain a stream, allowing the
81
+ data to be read decompressed.
82
+ :param data: random accessible data containing all required information
83
+ :parma offset: offset in to the data at which the object information is located
84
+ :param as_stream: if True, a stream object will be returned that can read
85
+ the data, otherwise you receive an info object only"""
86
+ data = cursor.use_region(offset).buffer()
87
+ type_id, uncomp_size, data_rela_offset = pack_object_header_info(data)
88
+ total_rela_offset = None # set later, actual offset until data stream begins
89
+ delta_info = None
90
+
91
+ # OFFSET DELTA
92
+ if type_id == OFS_DELTA:
93
+ i = data_rela_offset
94
+ c = byte_ord(data[i])
95
+ i += 1
96
+ delta_offset = c & 0x7f
97
+ while c & 0x80:
98
+ c = byte_ord(data[i])
99
+ i += 1
100
+ delta_offset += 1
101
+ delta_offset = (delta_offset << 7) + (c & 0x7f)
102
+ # END character loop
103
+ delta_info = delta_offset
104
+ total_rela_offset = i
105
+ # REF DELTA
106
+ elif type_id == REF_DELTA:
107
+ total_rela_offset = data_rela_offset + 20
108
+ delta_info = data[data_rela_offset:total_rela_offset]
109
+ # BASE OBJECT
110
+ else:
111
+ # assume its a base object
112
+ total_rela_offset = data_rela_offset
113
+ # END handle type id
114
+ abs_data_offset = offset + total_rela_offset
115
+ if as_stream:
116
+ stream = DecompressMemMapReader(data[total_rela_offset:], False, uncomp_size)
117
+ if delta_info is None:
118
+ return abs_data_offset, OPackStream(offset, type_id, uncomp_size, stream)
119
+ else:
120
+ return abs_data_offset, ODeltaPackStream(offset, type_id, uncomp_size, delta_info, stream)
121
+ else:
122
+ if delta_info is None:
123
+ return abs_data_offset, OPackInfo(offset, type_id, uncomp_size)
124
+ else:
125
+ return abs_data_offset, ODeltaPackInfo(offset, type_id, uncomp_size, delta_info)
126
+ # END handle info
127
+ # END handle stream
128
+
129
+
130
+ def write_stream_to_pack(read, write, zstream, base_crc=None):
131
+ """Copy a stream as read from read function, zip it, and write the result.
132
+ Count the number of written bytes and return it
133
+ :param base_crc: if not None, the crc will be the base for all compressed data
134
+ we consecutively write and generate a crc32 from. If None, no crc will be generated
135
+ :return: tuple(no bytes read, no bytes written, crc32) crc might be 0 if base_crc
136
+ was false"""
137
+ br = 0 # bytes read
138
+ bw = 0 # bytes written
139
+ want_crc = base_crc is not None
140
+ crc = 0
141
+ if want_crc:
142
+ crc = base_crc
143
+ # END initialize crc
144
+
145
+ while True:
146
+ chunk = read(chunk_size)
147
+ br += len(chunk)
148
+ compressed = zstream.compress(chunk)
149
+ bw += len(compressed)
150
+ write(compressed) # cannot assume return value
151
+
152
+ if want_crc:
153
+ crc = crc32(compressed, crc)
154
+ # END handle crc
155
+
156
+ if len(chunk) != chunk_size:
157
+ break
158
+ # END copy loop
159
+
160
+ compressed = zstream.flush()
161
+ bw += len(compressed)
162
+ write(compressed)
163
+ if want_crc:
164
+ crc = crc32(compressed, crc)
165
+ # END handle crc
166
+
167
+ return (br, bw, crc)
168
+
169
+
170
+ #} END utilities
171
+
172
+
173
+ class IndexWriter:
174
+
175
+ """Utility to cache index information, allowing to write all information later
176
+ in one go to the given stream
177
+ **Note:** currently only writes v2 indices"""
178
+ __slots__ = '_objs'
179
+
180
+ def __init__(self):
181
+ self._objs = list()
182
+
183
+ def append(self, binsha, crc, offset):
184
+ """Append one piece of object information"""
185
+ self._objs.append((binsha, crc, offset))
186
+
187
+ def write(self, pack_sha, write):
188
+ """Write the index file using the given write method
189
+ :param pack_sha: binary sha over the whole pack that we index
190
+ :return: sha1 binary sha over all index file contents"""
191
+ # sort for sha1 hash
192
+ self._objs.sort(key=lambda o: o[0])
193
+
194
+ sha_writer = FlexibleSha1Writer(write)
195
+ sha_write = sha_writer.write
196
+ sha_write(PackIndexFile.index_v2_signature)
197
+ sha_write(pack(">L", PackIndexFile.index_version_default))
198
+
199
+ # fanout
200
+ tmplist = list((0,) * 256) # fanout or list with 64 bit offsets
201
+ for t in self._objs:
202
+ tmplist[byte_ord(t[0][0])] += 1
203
+ # END prepare fanout
204
+ for i in range(255):
205
+ v = tmplist[i]
206
+ sha_write(pack('>L', v))
207
+ tmplist[i + 1] += v
208
+ # END write each fanout entry
209
+ sha_write(pack('>L', tmplist[255]))
210
+
211
+ # sha1 ordered
212
+ # save calls, that is push them into c
213
+ sha_write(b''.join(t[0] for t in self._objs))
214
+
215
+ # crc32
216
+ for t in self._objs:
217
+ sha_write(pack('>L', t[1] & 0xffffffff))
218
+ # END for each crc
219
+
220
+ tmplist = list()
221
+ # offset 32
222
+ for t in self._objs:
223
+ ofs = t[2]
224
+ if ofs > 0x7fffffff:
225
+ tmplist.append(ofs)
226
+ ofs = 0x80000000 + len(tmplist) - 1
227
+ # END handle 64 bit offsets
228
+ sha_write(pack('>L', ofs & 0xffffffff))
229
+ # END for each offset
230
+
231
+ # offset 64
232
+ for ofs in tmplist:
233
+ sha_write(pack(">Q", ofs))
234
+ # END for each offset
235
+
236
+ # trailer
237
+ assert(len(pack_sha) == 20)
238
+ sha_write(pack_sha)
239
+ sha = sha_writer.sha(as_hex=False)
240
+ write(sha)
241
+ return sha
242
+
243
+
244
+ class PackIndexFile(LazyMixin):
245
+
246
+ """A pack index provides offsets into the corresponding pack, allowing to find
247
+ locations for offsets faster."""
248
+
249
+ # Dont use slots as we dynamically bind functions for each version, need a dict for this
250
+ # The slots you see here are just to keep track of our instance variables
251
+ # __slots__ = ('_indexpath', '_fanout_table', '_cursor', '_version',
252
+ # '_sha_list_offset', '_crc_list_offset', '_pack_offset', '_pack_64_offset')
253
+
254
+ # used in v2 indices
255
+ _sha_list_offset = 8 + 1024
256
+ index_v2_signature = b'\xfftOc'
257
+ index_version_default = 2
258
+
259
+ def __init__(self, indexpath):
260
+ super().__init__()
261
+ self._indexpath = indexpath
262
+
263
+ def close(self):
264
+ mman.force_map_handle_removal_win(self._indexpath)
265
+ self._cursor = None
266
+
267
+ def _set_cache_(self, attr):
268
+ if attr == "_packfile_checksum":
269
+ self._packfile_checksum = self._cursor.map()[-40:-20]
270
+ elif attr == "_packfile_checksum":
271
+ self._packfile_checksum = self._cursor.map()[-20:]
272
+ elif attr == "_cursor":
273
+ # Note: We don't lock the file when reading as we cannot be sure
274
+ # that we can actually write to the location - it could be a read-only
275
+ # alternate for instance
276
+ self._cursor = mman.make_cursor(self._indexpath).use_region()
277
+ # We will assume that the index will always fully fit into memory !
278
+ if mman.window_size() > 0 and self._cursor.file_size() > mman.window_size():
279
+ raise AssertionError("The index file at %s is too large to fit into a mapped window (%i > %i). This is a limitation of the implementation" % (
280
+ self._indexpath, self._cursor.file_size(), mman.window_size()))
281
+ # END assert window size
282
+ else:
283
+ # now its time to initialize everything - if we are here, someone wants
284
+ # to access the fanout table or related properties
285
+
286
+ # CHECK VERSION
287
+ mmap = self._cursor.map()
288
+ self._version = (mmap[:4] == self.index_v2_signature and 2) or 1
289
+ if self._version == 2:
290
+ version_id = unpack_from(">L", mmap, 4)[0]
291
+ assert version_id == self._version, "Unsupported index version: %i" % version_id
292
+ # END assert version
293
+
294
+ # SETUP FUNCTIONS
295
+ # setup our functions according to the actual version
296
+ for fname in ('entry', 'offset', 'sha', 'crc'):
297
+ setattr(self, fname, getattr(self, "_%s_v%i" % (fname, self._version)))
298
+ # END for each function to initialize
299
+
300
+ # INITIALIZE DATA
301
+ # byte offset is 8 if version is 2, 0 otherwise
302
+ self._initialize()
303
+ # END handle attributes
304
+
305
+ #{ Access V1
306
+
307
+ def _entry_v1(self, i):
308
+ """:return: tuple(offset, binsha, 0)"""
309
+ return unpack_from(">L20s", self._cursor.map(), 1024 + i * 24) + (0, )
310
+
311
+ def _offset_v1(self, i):
312
+ """see ``_offset_v2``"""
313
+ return unpack_from(">L", self._cursor.map(), 1024 + i * 24)[0]
314
+
315
+ def _sha_v1(self, i):
316
+ """see ``_sha_v2``"""
317
+ base = 1024 + (i * 24) + 4
318
+ return self._cursor.map()[base:base + 20]
319
+
320
+ def _crc_v1(self, i):
321
+ """unsupported"""
322
+ return 0
323
+
324
+ #} END access V1
325
+
326
+ #{ Access V2
327
+ def _entry_v2(self, i):
328
+ """:return: tuple(offset, binsha, crc)"""
329
+ return (self._offset_v2(i), self._sha_v2(i), self._crc_v2(i))
330
+
331
+ def _offset_v2(self, i):
332
+ """:return: 32 or 64 byte offset into pack files. 64 byte offsets will only
333
+ be returned if the pack is larger than 4 GiB, or 2^32"""
334
+ offset = unpack_from(">L", self._cursor.map(), self._pack_offset + i * 4)[0]
335
+
336
+ # if the high-bit is set, this indicates that we have to lookup the offset
337
+ # in the 64 bit region of the file. The current offset ( lower 31 bits )
338
+ # are the index into it
339
+ if offset & 0x80000000:
340
+ offset = unpack_from(">Q", self._cursor.map(), self._pack_64_offset + (offset & ~0x80000000) * 8)[0]
341
+ # END handle 64 bit offset
342
+
343
+ return offset
344
+
345
+ def _sha_v2(self, i):
346
+ """:return: sha at the given index of this file index instance"""
347
+ base = self._sha_list_offset + i * 20
348
+ return self._cursor.map()[base:base + 20]
349
+
350
+ def _crc_v2(self, i):
351
+ """:return: 4 bytes crc for the object at index i"""
352
+ return unpack_from(">L", self._cursor.map(), self._crc_list_offset + i * 4)[0]
353
+
354
+ #} END access V2
355
+
356
+ #{ Initialization
357
+
358
+ def _initialize(self):
359
+ """initialize base data"""
360
+ self._fanout_table = self._read_fanout((self._version == 2) * 8)
361
+
362
+ if self._version == 2:
363
+ self._crc_list_offset = self._sha_list_offset + self.size() * 20
364
+ self._pack_offset = self._crc_list_offset + self.size() * 4
365
+ self._pack_64_offset = self._pack_offset + self.size() * 4
366
+ # END setup base
367
+
368
+ def _read_fanout(self, byte_offset):
369
+ """Generate a fanout table from our data"""
370
+ d = self._cursor.map()
371
+ out = list()
372
+ append = out.append
373
+ for i in range(256):
374
+ append(unpack_from('>L', d, byte_offset + i * 4)[0])
375
+ # END for each entry
376
+ return out
377
+
378
+ #} END initialization
379
+
380
+ #{ Properties
381
+ def version(self):
382
+ return self._version
383
+
384
+ def size(self):
385
+ """:return: amount of objects referred to by this index"""
386
+ return self._fanout_table[255]
387
+
388
+ def path(self):
389
+ """:return: path to the packindexfile"""
390
+ return self._indexpath
391
+
392
+ def packfile_checksum(self):
393
+ """:return: 20 byte sha representing the sha1 hash of the pack file"""
394
+ return self._cursor.map()[-40:-20]
395
+
396
+ def indexfile_checksum(self):
397
+ """:return: 20 byte sha representing the sha1 hash of this index file"""
398
+ return self._cursor.map()[-20:]
399
+
400
+ def offsets(self):
401
+ """:return: sequence of all offsets in the order in which they were written
402
+
403
+ **Note:** return value can be random accessed, but may be immmutable"""
404
+ if self._version == 2:
405
+ # read stream to array, convert to tuple
406
+ a = array.array('I') # 4 byte unsigned int, long are 8 byte on 64 bit it appears
407
+ a.frombytes(self._cursor.map()[self._pack_offset:self._pack_64_offset])
408
+
409
+ # networkbyteorder to something array likes more
410
+ if sys.byteorder == 'little':
411
+ a.byteswap()
412
+ return a
413
+ else:
414
+ return tuple(self.offset(index) for index in range(self.size()))
415
+ # END handle version
416
+
417
+ def sha_to_index(self, sha):
418
+ """
419
+ :return: index usable with the ``offset`` or ``entry`` method, or None
420
+ if the sha was not found in this pack index
421
+ :param sha: 20 byte sha to lookup"""
422
+ first_byte = byte_ord(sha[0])
423
+ get_sha = self.sha
424
+ lo = 0 # lower index, the left bound of the bisection
425
+ if first_byte != 0:
426
+ lo = self._fanout_table[first_byte - 1]
427
+ hi = self._fanout_table[first_byte] # the upper, right bound of the bisection
428
+
429
+ # bisect until we have the sha
430
+ while lo < hi:
431
+ mid = (lo + hi) // 2
432
+ mid_sha = get_sha(mid)
433
+ if sha < mid_sha:
434
+ hi = mid
435
+ elif sha == mid_sha:
436
+ return mid
437
+ else:
438
+ lo = mid + 1
439
+ # END handle midpoint
440
+ # END bisect
441
+ return None
442
+
443
+ def partial_sha_to_index(self, partial_bin_sha, canonical_length):
444
+ """
445
+ :return: index as in `sha_to_index` or None if the sha was not found in this
446
+ index file
447
+ :param partial_bin_sha: an at least two bytes of a partial binary sha as bytes
448
+ :param canonical_length: length of the original hexadecimal representation of the
449
+ given partial binary sha
450
+ :raise AmbiguousObjectName:"""
451
+ if len(partial_bin_sha) < 2:
452
+ raise ValueError("Require at least 2 bytes of partial sha")
453
+
454
+ assert isinstance(partial_bin_sha, bytes), "partial_bin_sha must be bytes"
455
+ first_byte = byte_ord(partial_bin_sha[0])
456
+
457
+ get_sha = self.sha
458
+ lo = 0 # lower index, the left bound of the bisection
459
+ if first_byte != 0:
460
+ lo = self._fanout_table[first_byte - 1]
461
+ hi = self._fanout_table[first_byte] # the upper, right bound of the bisection
462
+
463
+ # fill the partial to full 20 bytes
464
+ filled_sha = partial_bin_sha + NULL_BYTE * (20 - len(partial_bin_sha))
465
+
466
+ # find lowest
467
+ while lo < hi:
468
+ mid = (lo + hi) // 2
469
+ mid_sha = get_sha(mid)
470
+ if filled_sha < mid_sha:
471
+ hi = mid
472
+ elif filled_sha == mid_sha:
473
+ # perfect match
474
+ lo = mid
475
+ break
476
+ else:
477
+ lo = mid + 1
478
+ # END handle midpoint
479
+ # END bisect
480
+
481
+ if lo < self.size():
482
+ cur_sha = get_sha(lo)
483
+ if is_equal_canonical_sha(canonical_length, partial_bin_sha, cur_sha):
484
+ next_sha = None
485
+ if lo + 1 < self.size():
486
+ next_sha = get_sha(lo + 1)
487
+ if next_sha and next_sha == cur_sha:
488
+ raise AmbiguousObjectName(partial_bin_sha)
489
+ return lo
490
+ # END if we have a match
491
+ # END if we found something
492
+ return None
493
+
494
+ if 'PackIndexFile_sha_to_index' in globals():
495
+ # NOTE: Its just about 25% faster, the major bottleneck might be the attr
496
+ # accesses
497
+ def sha_to_index(self, sha):
498
+ return PackIndexFile_sha_to_index(self, sha)
499
+ # END redefine heavy-hitter with c version
500
+
501
+ #} END properties
502
+
503
+
504
+ class PackFile(LazyMixin):
505
+
506
+ """A pack is a file written according to the Version 2 for git packs
507
+
508
+ As we currently use memory maps, it could be assumed that the maximum size of
509
+ packs therefore is 32 bit on 32 bit systems. On 64 bit systems, this should be
510
+ fine though.
511
+
512
+ **Note:** at some point, this might be implemented using streams as well, or
513
+ streams are an alternate path in the case memory maps cannot be created
514
+ for some reason - one clearly doesn't want to read 10GB at once in that
515
+ case"""
516
+
517
+ __slots__ = ('_packpath', '_cursor', '_size', '_version')
518
+ pack_signature = 0x5041434b # 'PACK'
519
+ pack_version_default = 2
520
+
521
+ # offset into our data at which the first object starts
522
+ first_object_offset = 3 * 4 # header bytes
523
+ footer_size = 20 # final sha
524
+
525
+ def __init__(self, packpath):
526
+ self._packpath = packpath
527
+
528
+ def close(self):
529
+ mman.force_map_handle_removal_win(self._packpath)
530
+ self._cursor = None
531
+
532
+ def _set_cache_(self, attr):
533
+ # we fill the whole cache, whichever attribute gets queried first
534
+ self._cursor = mman.make_cursor(self._packpath).use_region()
535
+
536
+ # read the header information
537
+ type_id, self._version, self._size = unpack_from(">LLL", self._cursor.map(), 0)
538
+
539
+ # TODO: figure out whether we should better keep the lock, or maybe
540
+ # add a .keep file instead ?
541
+ if type_id != self.pack_signature:
542
+ raise ParseError("Invalid pack signature: %i" % type_id)
543
+
544
+ def _iter_objects(self, start_offset, as_stream=True):
545
+ """Handle the actual iteration of objects within this pack"""
546
+ c = self._cursor
547
+ content_size = c.file_size() - self.footer_size
548
+ cur_offset = start_offset or self.first_object_offset
549
+
550
+ null = NullStream()
551
+ while cur_offset < content_size:
552
+ data_offset, ostream = pack_object_at(c, cur_offset, True)
553
+ # scrub the stream to the end - this decompresses the object, but yields
554
+ # the amount of compressed bytes we need to get to the next offset
555
+
556
+ stream_copy(ostream.read, null.write, ostream.size, chunk_size)
557
+ assert ostream.stream._br == ostream.size
558
+ cur_offset += (data_offset - ostream.pack_offset) + ostream.stream.compressed_bytes_read()
559
+
560
+ # if a stream is requested, reset it beforehand
561
+ # Otherwise return the Stream object directly, its derived from the
562
+ # info object
563
+ if as_stream:
564
+ ostream.stream.seek(0)
565
+ yield ostream
566
+ # END until we have read everything
567
+
568
+ #{ Pack Information
569
+
570
+ def size(self):
571
+ """:return: The amount of objects stored in this pack"""
572
+ return self._size
573
+
574
+ def version(self):
575
+ """:return: the version of this pack"""
576
+ return self._version
577
+
578
+ def data(self):
579
+ """
580
+ :return: read-only data of this pack. It provides random access and usually
581
+ is a memory map.
582
+ :note: This method is unsafe as it returns a window into a file which might be larger than than the actual window size"""
583
+ # can use map as we are starting at offset 0. Otherwise we would have to use buffer()
584
+ return self._cursor.use_region().map()
585
+
586
+ def checksum(self):
587
+ """:return: 20 byte sha1 hash on all object sha's contained in this file"""
588
+ return self._cursor.use_region(self._cursor.file_size() - 20).buffer()[:]
589
+
590
+ def path(self):
591
+ """:return: path to the packfile"""
592
+ return self._packpath
593
+ #} END pack information
594
+
595
+ #{ Pack Specific
596
+
597
+ def collect_streams(self, offset):
598
+ """
599
+ :return: list of pack streams which are required to build the object
600
+ at the given offset. The first entry of the list is the object at offset,
601
+ the last one is either a full object, or a REF_Delta stream. The latter
602
+ type needs its reference object to be locked up in an ODB to form a valid
603
+ delta chain.
604
+ If the object at offset is no delta, the size of the list is 1.
605
+ :param offset: specifies the first byte of the object within this pack"""
606
+ out = list()
607
+ c = self._cursor
608
+ while True:
609
+ ostream = pack_object_at(c, offset, True)[1]
610
+ out.append(ostream)
611
+ if ostream.type_id == OFS_DELTA:
612
+ offset = ostream.pack_offset - ostream.delta_info
613
+ else:
614
+ # the only thing we can lookup are OFFSET deltas. Everything
615
+ # else is either an object, or a ref delta, in the latter
616
+ # case someone else has to find it
617
+ break
618
+ # END handle type
619
+ # END while chaining streams
620
+ return out
621
+
622
+ #} END pack specific
623
+
624
+ #{ Read-Database like Interface
625
+
626
+ def info(self, offset):
627
+ """Retrieve information about the object at the given file-absolute offset
628
+
629
+ :param offset: byte offset
630
+ :return: OPackInfo instance, the actual type differs depending on the type_id attribute"""
631
+ return pack_object_at(self._cursor, offset or self.first_object_offset, False)[1]
632
+
633
+ def stream(self, offset):
634
+ """Retrieve an object at the given file-relative offset as stream along with its information
635
+
636
+ :param offset: byte offset
637
+ :return: OPackStream instance, the actual type differs depending on the type_id attribute"""
638
+ return pack_object_at(self._cursor, offset or self.first_object_offset, True)[1]
639
+
640
+ def stream_iter(self, start_offset=0):
641
+ """
642
+ :return: iterator yielding OPackStream compatible instances, allowing
643
+ to access the data in the pack directly.
644
+ :param start_offset: offset to the first object to iterate. If 0, iteration
645
+ starts at the very first object in the pack.
646
+
647
+ **Note:** Iterating a pack directly is costly as the datastream has to be decompressed
648
+ to determine the bounds between the objects"""
649
+ return self._iter_objects(start_offset, as_stream=True)
650
+
651
+ #} END Read-Database like Interface
652
+
653
+
654
+ class PackEntity(LazyMixin):
655
+
656
+ """Combines the PackIndexFile and the PackFile into one, allowing the
657
+ actual objects to be resolved and iterated"""
658
+
659
+ __slots__ = ('_index', # our index file
660
+ '_pack', # our pack file
661
+ '_offset_map' # on demand dict mapping one offset to the next consecutive one
662
+ )
663
+
664
+ IndexFileCls = PackIndexFile
665
+ PackFileCls = PackFile
666
+
667
+ def __init__(self, pack_or_index_path):
668
+ """Initialize ourselves with the path to the respective pack or index file"""
669
+ basename, ext = os.path.splitext(pack_or_index_path)
670
+ self._index = self.IndexFileCls("%s.idx" % basename) # PackIndexFile instance
671
+ self._pack = self.PackFileCls("%s.pack" % basename) # corresponding PackFile instance
672
+
673
+ def close(self):
674
+ self._index.close()
675
+ self._pack.close()
676
+
677
+ def _set_cache_(self, attr):
678
+ # currently this can only be _offset_map
679
+ # TODO: make this a simple sorted offset array which can be bisected
680
+ # to find the respective entry, from which we can take a +1 easily
681
+ # This might be slower, but should also be much lighter in memory !
682
+ offsets_sorted = sorted(self._index.offsets())
683
+ last_offset = len(self._pack.data()) - self._pack.footer_size
684
+ assert offsets_sorted, "Cannot handle empty indices"
685
+
686
+ offset_map = None
687
+ if len(offsets_sorted) == 1:
688
+ offset_map = {offsets_sorted[0]: last_offset}
689
+ else:
690
+ iter_offsets = iter(offsets_sorted)
691
+ iter_offsets_plus_one = iter(offsets_sorted)
692
+ next(iter_offsets_plus_one)
693
+ consecutive = zip(iter_offsets, iter_offsets_plus_one)
694
+
695
+ offset_map = dict(consecutive)
696
+
697
+ # the last offset is not yet set
698
+ offset_map[offsets_sorted[-1]] = last_offset
699
+ # END handle offset amount
700
+ self._offset_map = offset_map
701
+
702
+ def _sha_to_index(self, sha):
703
+ """:return: index for the given sha, or raise"""
704
+ index = self._index.sha_to_index(sha)
705
+ if index is None:
706
+ raise BadObject(sha)
707
+ return index
708
+
709
+ def _iter_objects(self, as_stream):
710
+ """Iterate over all objects in our index and yield their OInfo or OStream instences"""
711
+ _sha = self._index.sha
712
+ _object = self._object
713
+ for index in range(self._index.size()):
714
+ yield _object(_sha(index), as_stream, index)
715
+ # END for each index
716
+
717
+ def _object(self, sha, as_stream, index=-1):
718
+ """:return: OInfo or OStream object providing information about the given sha
719
+ :param index: if not -1, its assumed to be the sha's index in the IndexFile"""
720
+ # its a little bit redundant here, but it needs to be efficient
721
+ if index < 0:
722
+ index = self._sha_to_index(sha)
723
+ if sha is None:
724
+ sha = self._index.sha(index)
725
+ # END assure sha is present ( in output )
726
+ offset = self._index.offset(index)
727
+ type_id, uncomp_size, data_rela_offset = pack_object_header_info(self._pack._cursor.use_region(offset).buffer())
728
+ if as_stream:
729
+ if type_id not in delta_types:
730
+ packstream = self._pack.stream(offset)
731
+ return OStream(sha, packstream.type, packstream.size, packstream.stream)
732
+ # END handle non-deltas
733
+
734
+ # produce a delta stream containing all info
735
+ # To prevent it from applying the deltas when querying the size,
736
+ # we extract it from the delta stream ourselves
737
+ streams = self.collect_streams_at_offset(offset)
738
+ dstream = DeltaApplyReader.new(streams)
739
+
740
+ return ODeltaStream(sha, dstream.type, None, dstream)
741
+ else:
742
+ if type_id not in delta_types:
743
+ return OInfo(sha, type_id_to_type_map[type_id], uncomp_size)
744
+ # END handle non-deltas
745
+
746
+ # deltas are a little tougher - unpack the first bytes to obtain
747
+ # the actual target size, as opposed to the size of the delta data
748
+ streams = self.collect_streams_at_offset(offset)
749
+ buf = streams[0].read(512)
750
+ offset, src_size = msb_size(buf)
751
+ offset, target_size = msb_size(buf, offset)
752
+
753
+ # collect the streams to obtain the actual object type
754
+ if streams[-1].type_id in delta_types:
755
+ raise BadObject(sha, "Could not resolve delta object")
756
+ return OInfo(sha, streams[-1].type, target_size)
757
+ # END handle stream
758
+
759
+ #{ Read-Database like Interface
760
+
761
+ def info(self, sha):
762
+ """Retrieve information about the object identified by the given sha
763
+
764
+ :param sha: 20 byte sha1
765
+ :raise BadObject:
766
+ :return: OInfo instance, with 20 byte sha"""
767
+ return self._object(sha, False)
768
+
769
+ def stream(self, sha):
770
+ """Retrieve an object stream along with its information as identified by the given sha
771
+
772
+ :param sha: 20 byte sha1
773
+ :raise BadObject:
774
+ :return: OStream instance, with 20 byte sha"""
775
+ return self._object(sha, True)
776
+
777
+ def info_at_index(self, index):
778
+ """As ``info``, but uses a PackIndexFile compatible index to refer to the object"""
779
+ return self._object(None, False, index)
780
+
781
+ def stream_at_index(self, index):
782
+ """As ``stream``, but uses a PackIndexFile compatible index to refer to the
783
+ object"""
784
+ return self._object(None, True, index)
785
+
786
+ #} END Read-Database like Interface
787
+
788
+ #{ Interface
789
+
790
+ def pack(self):
791
+ """:return: the underlying pack file instance"""
792
+ return self._pack
793
+
794
+ def index(self):
795
+ """:return: the underlying pack index file instance"""
796
+ return self._index
797
+
798
+ def is_valid_stream(self, sha, use_crc=False):
799
+ """
800
+ Verify that the stream at the given sha is valid.
801
+
802
+ :param use_crc: if True, the index' crc is run over the compressed stream of
803
+ the object, which is much faster than checking the sha1. It is also
804
+ more prone to unnoticed corruption or manipulation.
805
+ :param sha: 20 byte sha1 of the object whose stream to verify
806
+ whether the compressed stream of the object is valid. If it is
807
+ a delta, this only verifies that the delta's data is valid, not the
808
+ data of the actual undeltified object, as it depends on more than
809
+ just this stream.
810
+ If False, the object will be decompressed and the sha generated. It must
811
+ match the given sha
812
+
813
+ :return: True if the stream is valid
814
+ :raise UnsupportedOperation: If the index is version 1 only
815
+ :raise BadObject: sha was not found"""
816
+ if use_crc:
817
+ if self._index.version() < 2:
818
+ raise UnsupportedOperation("Version 1 indices do not contain crc's, verify by sha instead")
819
+ # END handle index version
820
+
821
+ index = self._sha_to_index(sha)
822
+ offset = self._index.offset(index)
823
+ next_offset = self._offset_map[offset]
824
+ crc_value = self._index.crc(index)
825
+
826
+ # create the current crc value, on the compressed object data
827
+ # Read it in chunks, without copying the data
828
+ crc_update = zlib.crc32
829
+ pack_data = self._pack.data()
830
+ cur_pos = offset
831
+ this_crc_value = 0
832
+ while cur_pos < next_offset:
833
+ rbound = min(cur_pos + chunk_size, next_offset)
834
+ size = rbound - cur_pos
835
+ this_crc_value = crc_update(pack_data[cur_pos:cur_pos + size], this_crc_value)
836
+ cur_pos += size
837
+ # END window size loop
838
+
839
+ # crc returns signed 32 bit numbers, the AND op forces it into unsigned
840
+ # mode ... wow, sneaky, from dulwich.
841
+ return (this_crc_value & 0xffffffff) == crc_value
842
+ else:
843
+ shawriter = Sha1Writer()
844
+ stream = self._object(sha, as_stream=True)
845
+ # write a loose object, which is the basis for the sha
846
+ write_object(stream.type, stream.size, stream.read, shawriter.write)
847
+
848
+ assert shawriter.sha(as_hex=False) == sha
849
+ return shawriter.sha(as_hex=False) == sha
850
+ # END handle crc/sha verification
851
+ return True
852
+
853
+ def info_iter(self):
854
+ """
855
+ :return: Iterator over all objects in this pack. The iterator yields
856
+ OInfo instances"""
857
+ return self._iter_objects(as_stream=False)
858
+
859
+ def stream_iter(self):
860
+ """
861
+ :return: iterator over all objects in this pack. The iterator yields
862
+ OStream instances"""
863
+ return self._iter_objects(as_stream=True)
864
+
865
+ def collect_streams_at_offset(self, offset):
866
+ """
867
+ As the version in the PackFile, but can resolve REF deltas within this pack
868
+ For more info, see ``collect_streams``
869
+
870
+ :param offset: offset into the pack file at which the object can be found"""
871
+ streams = self._pack.collect_streams(offset)
872
+
873
+ # try to resolve the last one if needed. It is assumed to be either
874
+ # a REF delta, or a base object, as OFFSET deltas are resolved by the pack
875
+ if streams[-1].type_id == REF_DELTA:
876
+ stream = streams[-1]
877
+ while stream.type_id in delta_types:
878
+ if stream.type_id == REF_DELTA:
879
+ # smmap can return memory view objects, which can't be compared as buffers/bytes can ...
880
+ if isinstance(stream.delta_info, memoryview):
881
+ sindex = self._index.sha_to_index(stream.delta_info.tobytes())
882
+ else:
883
+ sindex = self._index.sha_to_index(stream.delta_info)
884
+ if sindex is None:
885
+ break
886
+ stream = self._pack.stream(self._index.offset(sindex))
887
+ streams.append(stream)
888
+ else:
889
+ # must be another OFS DELTA - this could happen if a REF
890
+ # delta we resolve previously points to an OFS delta. Who
891
+ # would do that ;) ? We can handle it though
892
+ stream = self._pack.stream(stream.delta_info)
893
+ streams.append(stream)
894
+ # END handle ref delta
895
+ # END resolve ref streams
896
+ # END resolve streams
897
+
898
+ return streams
899
+
900
+ def collect_streams(self, sha):
901
+ """
902
+ As ``PackFile.collect_streams``, but takes a sha instead of an offset.
903
+ Additionally, ref_delta streams will be resolved within this pack.
904
+ If this is not possible, the stream will be left alone, hence it is adivsed
905
+ to check for unresolved ref-deltas and resolve them before attempting to
906
+ construct a delta stream.
907
+
908
+ :param sha: 20 byte sha1 specifying the object whose related streams you want to collect
909
+ :return: list of streams, first being the actual object delta, the last being
910
+ a possibly unresolved base object.
911
+ :raise BadObject:"""
912
+ return self.collect_streams_at_offset(self._index.offset(self._sha_to_index(sha)))
913
+
914
+ @classmethod
915
+ def write_pack(cls, object_iter, pack_write, index_write=None,
916
+ object_count=None, zlib_compression=zlib.Z_BEST_SPEED):
917
+ """
918
+ Create a new pack by putting all objects obtained by the object_iterator
919
+ into a pack which is written using the pack_write method.
920
+ The respective index is produced as well if index_write is not Non.
921
+
922
+ :param object_iter: iterator yielding odb output objects
923
+ :param pack_write: function to receive strings to write into the pack stream
924
+ :param indx_write: if not None, the function writes the index file corresponding
925
+ to the pack.
926
+ :param object_count: if you can provide the amount of objects in your iteration,
927
+ this would be the place to put it. Otherwise we have to pre-iterate and store
928
+ all items into a list to get the number, which uses more memory than necessary.
929
+ :param zlib_compression: the zlib compression level to use
930
+ :return: tuple(pack_sha, index_binsha) binary sha over all the contents of the pack
931
+ and over all contents of the index. If index_write was None, index_binsha will be None
932
+
933
+ **Note:** The destination of the write functions is up to the user. It could
934
+ be a socket, or a file for instance
935
+
936
+ **Note:** writes only undeltified objects"""
937
+ objs = object_iter
938
+ if not object_count:
939
+ if not isinstance(object_iter, (tuple, list)):
940
+ objs = list(object_iter)
941
+ # END handle list type
942
+ object_count = len(objs)
943
+ # END handle object
944
+
945
+ pack_writer = FlexibleSha1Writer(pack_write)
946
+ pwrite = pack_writer.write
947
+ ofs = 0 # current offset into the pack file
948
+ index = None
949
+ wants_index = index_write is not None
950
+
951
+ # write header
952
+ pwrite(pack('>LLL', PackFile.pack_signature, PackFile.pack_version_default, object_count))
953
+ ofs += 12
954
+
955
+ if wants_index:
956
+ index = IndexWriter()
957
+ # END handle index header
958
+
959
+ actual_count = 0
960
+ for obj in objs:
961
+ actual_count += 1
962
+ crc = 0
963
+
964
+ # object header
965
+ hdr = create_pack_object_header(obj.type_id, obj.size)
966
+ if index_write:
967
+ crc = crc32(hdr)
968
+ else:
969
+ crc = None
970
+ # END handle crc
971
+ pwrite(hdr)
972
+
973
+ # data stream
974
+ zstream = zlib.compressobj(zlib_compression)
975
+ ostream = obj.stream
976
+ br, bw, crc = write_stream_to_pack(ostream.read, pwrite, zstream, base_crc=crc)
977
+ assert(br == obj.size)
978
+ if wants_index:
979
+ index.append(obj.binsha, crc, ofs)
980
+ # END handle index
981
+
982
+ ofs += len(hdr) + bw
983
+ if actual_count == object_count:
984
+ break
985
+ # END abort once we are done
986
+ # END for each object
987
+
988
+ if actual_count != object_count:
989
+ raise ValueError(
990
+ "Expected to write %i objects into pack, but received only %i from iterators" % (object_count, actual_count))
991
+ # END count assertion
992
+
993
+ # write footer
994
+ pack_sha = pack_writer.sha(as_hex=False)
995
+ assert len(pack_sha) == 20
996
+ pack_write(pack_sha)
997
+ ofs += len(pack_sha) # just for completeness ;)
998
+
999
+ index_sha = None
1000
+ if wants_index:
1001
+ index_sha = index.write(pack_sha, index_write)
1002
+ # END handle index
1003
+
1004
+ return pack_sha, index_sha
1005
+
1006
+ @classmethod
1007
+ def create(cls, object_iter, base_dir, object_count=None, zlib_compression=zlib.Z_BEST_SPEED):
1008
+ """Create a new on-disk entity comprised of a properly named pack file and a properly named
1009
+ and corresponding index file. The pack contains all OStream objects contained in object iter.
1010
+ :param base_dir: directory which is to contain the files
1011
+ :return: PackEntity instance initialized with the new pack
1012
+
1013
+ **Note:** for more information on the other parameters see the write_pack method"""
1014
+ pack_fd, pack_path = tempfile.mkstemp('', 'pack', base_dir)
1015
+ index_fd, index_path = tempfile.mkstemp('', 'index', base_dir)
1016
+ pack_write = lambda d: os.write(pack_fd, d)
1017
+ index_write = lambda d: os.write(index_fd, d)
1018
+
1019
+ pack_binsha, index_binsha = cls.write_pack(object_iter, pack_write, index_write, object_count, zlib_compression)
1020
+ os.close(pack_fd)
1021
+ os.close(index_fd)
1022
+
1023
+ fmt = "pack-%s.%s"
1024
+ new_pack_path = os.path.join(base_dir, fmt % (bin_to_hex(pack_binsha), 'pack'))
1025
+ new_index_path = os.path.join(base_dir, fmt % (bin_to_hex(pack_binsha), 'idx'))
1026
+ os.rename(pack_path, new_pack_path)
1027
+ os.rename(index_path, new_index_path)
1028
+
1029
+ return cls(new_pack_path)
1030
+
1031
+ #} END interface
lib/python3.12/site-packages/gitdb/stream.py ADDED
@@ -0,0 +1,730 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+
6
+ from io import BytesIO
7
+
8
+ import mmap
9
+ import os
10
+ import sys
11
+ import zlib
12
+
13
+ from gitdb.fun import (
14
+ msb_size,
15
+ stream_copy,
16
+ apply_delta_data,
17
+ connect_deltas,
18
+ delta_types
19
+ )
20
+
21
+ from gitdb.util import (
22
+ allocate_memory,
23
+ LazyMixin,
24
+ make_sha,
25
+ write,
26
+ close,
27
+ )
28
+
29
+ from gitdb.const import NULL_BYTE, BYTE_SPACE
30
+ from gitdb.utils.encoding import force_bytes
31
+
32
+ has_perf_mod = False
33
+ try:
34
+ from gitdb_speedups._perf import apply_delta as c_apply_delta
35
+ has_perf_mod = True
36
+ except ImportError:
37
+ pass
38
+
39
+ __all__ = ('DecompressMemMapReader', 'FDCompressedSha1Writer', 'DeltaApplyReader',
40
+ 'Sha1Writer', 'FlexibleSha1Writer', 'ZippedStoreShaWriter', 'FDCompressedSha1Writer',
41
+ 'FDStream', 'NullStream')
42
+
43
+
44
+ #{ RO Streams
45
+
46
+ class DecompressMemMapReader(LazyMixin):
47
+
48
+ """Reads data in chunks from a memory map and decompresses it. The client sees
49
+ only the uncompressed data, respective file-like read calls are handling on-demand
50
+ buffered decompression accordingly
51
+
52
+ A constraint on the total size of bytes is activated, simulating
53
+ a logical file within a possibly larger physical memory area
54
+
55
+ To read efficiently, you clearly don't want to read individual bytes, instead,
56
+ read a few kilobytes at least.
57
+
58
+ **Note:** The chunk-size should be carefully selected as it will involve quite a bit
59
+ of string copying due to the way the zlib is implemented. Its very wasteful,
60
+ hence we try to find a good tradeoff between allocation time and number of
61
+ times we actually allocate. An own zlib implementation would be good here
62
+ to better support streamed reading - it would only need to keep the mmap
63
+ and decompress it into chunks, that's all ... """
64
+ __slots__ = ('_m', '_zip', '_buf', '_buflen', '_br', '_cws', '_cwe', '_s', '_close',
65
+ '_cbr', '_phi')
66
+
67
+ max_read_size = 512 * 1024 # currently unused
68
+
69
+ def __init__(self, m, close_on_deletion, size=None):
70
+ """Initialize with mmap for stream reading
71
+ :param m: must be content data - use new if you have object data and no size"""
72
+ self._m = m
73
+ self._zip = zlib.decompressobj()
74
+ self._buf = None # buffer of decompressed bytes
75
+ self._buflen = 0 # length of bytes in buffer
76
+ if size is not None:
77
+ self._s = size # size of uncompressed data to read in total
78
+ self._br = 0 # num uncompressed bytes read
79
+ self._cws = 0 # start byte of compression window
80
+ self._cwe = 0 # end byte of compression window
81
+ self._cbr = 0 # number of compressed bytes read
82
+ self._phi = False # is True if we parsed the header info
83
+ self._close = close_on_deletion # close the memmap on deletion ?
84
+
85
+ def _set_cache_(self, attr):
86
+ assert attr == '_s'
87
+ # only happens for size, which is a marker to indicate we still
88
+ # have to parse the header from the stream
89
+ self._parse_header_info()
90
+
91
+ def __del__(self):
92
+ self.close()
93
+
94
+ def _parse_header_info(self):
95
+ """If this stream contains object data, parse the header info and skip the
96
+ stream to a point where each read will yield object content
97
+
98
+ :return: parsed type_string, size"""
99
+ # read header
100
+ # should really be enough, cgit uses 8192 I believe
101
+ # And for good reason !! This needs to be that high for the header to be read correctly in all cases
102
+ maxb = 8192
103
+ self._s = maxb
104
+ hdr = self.read(maxb)
105
+ hdrend = hdr.find(NULL_BYTE)
106
+ typ, size = hdr[:hdrend].split(BYTE_SPACE)
107
+ size = int(size)
108
+ self._s = size
109
+
110
+ # adjust internal state to match actual header length that we ignore
111
+ # The buffer will be depleted first on future reads
112
+ self._br = 0
113
+ hdrend += 1
114
+ self._buf = BytesIO(hdr[hdrend:])
115
+ self._buflen = len(hdr) - hdrend
116
+
117
+ self._phi = True
118
+
119
+ return typ, size
120
+
121
+ #{ Interface
122
+
123
+ @classmethod
124
+ def new(self, m, close_on_deletion=False):
125
+ """Create a new DecompressMemMapReader instance for acting as a read-only stream
126
+ This method parses the object header from m and returns the parsed
127
+ type and size, as well as the created stream instance.
128
+
129
+ :param m: memory map on which to operate. It must be object data ( header + contents )
130
+ :param close_on_deletion: if True, the memory map will be closed once we are
131
+ being deleted"""
132
+ inst = DecompressMemMapReader(m, close_on_deletion, 0)
133
+ typ, size = inst._parse_header_info()
134
+ return typ, size, inst
135
+
136
+ def data(self):
137
+ """:return: random access compatible data we are working on"""
138
+ return self._m
139
+
140
+ def close(self):
141
+ """Close our underlying stream of compressed bytes if this was allowed during initialization
142
+ :return: True if we closed the underlying stream
143
+ :note: can be called safely
144
+ """
145
+ if self._close:
146
+ if hasattr(self._m, 'close'):
147
+ self._m.close()
148
+ self._close = False
149
+ # END handle resource freeing
150
+
151
+ def compressed_bytes_read(self):
152
+ """
153
+ :return: number of compressed bytes read. This includes the bytes it
154
+ took to decompress the header ( if there was one )"""
155
+ # ABSTRACT: When decompressing a byte stream, it can be that the first
156
+ # x bytes which were requested match the first x bytes in the loosely
157
+ # compressed datastream. This is the worst-case assumption that the reader
158
+ # does, it assumes that it will get at least X bytes from X compressed bytes
159
+ # in call cases.
160
+ # The caveat is that the object, according to our known uncompressed size,
161
+ # is already complete, but there are still some bytes left in the compressed
162
+ # stream that contribute to the amount of compressed bytes.
163
+ # How can we know that we are truly done, and have read all bytes we need
164
+ # to read ?
165
+ # Without help, we cannot know, as we need to obtain the status of the
166
+ # decompression. If it is not finished, we need to decompress more data
167
+ # until it is finished, to yield the actual number of compressed bytes
168
+ # belonging to the decompressed object
169
+ # We are using a custom zlib module for this, if its not present,
170
+ # we try to put in additional bytes up for decompression if feasible
171
+ # and check for the unused_data.
172
+
173
+ # Only scrub the stream forward if we are officially done with the
174
+ # bytes we were to have.
175
+ if self._br == self._s and not self._zip.unused_data:
176
+ # manipulate the bytes-read to allow our own read method to continue
177
+ # but keep the window at its current position
178
+ self._br = 0
179
+ if hasattr(self._zip, 'status'):
180
+ while self._zip.status == zlib.Z_OK:
181
+ self.read(mmap.PAGESIZE)
182
+ # END scrub-loop custom zlib
183
+ else:
184
+ # pass in additional pages, until we have unused data
185
+ while not self._zip.unused_data and self._cbr != len(self._m):
186
+ self.read(mmap.PAGESIZE)
187
+ # END scrub-loop default zlib
188
+ # END handle stream scrubbing
189
+
190
+ # reset bytes read, just to be sure
191
+ self._br = self._s
192
+ # END handle stream scrubbing
193
+
194
+ # unused data ends up in the unconsumed tail, which was removed
195
+ # from the count already
196
+ return self._cbr
197
+
198
+ #} END interface
199
+
200
+ def seek(self, offset, whence=getattr(os, 'SEEK_SET', 0)):
201
+ """Allows to reset the stream to restart reading
202
+ :raise ValueError: If offset and whence are not 0"""
203
+ if offset != 0 or whence != getattr(os, 'SEEK_SET', 0):
204
+ raise ValueError("Can only seek to position 0")
205
+ # END handle offset
206
+
207
+ self._zip = zlib.decompressobj()
208
+ self._br = self._cws = self._cwe = self._cbr = 0
209
+ if self._phi:
210
+ self._phi = False
211
+ del(self._s) # trigger header parsing on first access
212
+ # END skip header
213
+
214
+ def read(self, size=-1):
215
+ if size < 1:
216
+ size = self._s - self._br
217
+ else:
218
+ size = min(size, self._s - self._br)
219
+ # END clamp size
220
+
221
+ if size == 0:
222
+ return b''
223
+ # END handle depletion
224
+
225
+ # deplete the buffer, then just continue using the decompress object
226
+ # which has an own buffer. We just need this to transparently parse the
227
+ # header from the zlib stream
228
+ dat = b''
229
+ if self._buf:
230
+ if self._buflen >= size:
231
+ # have enough data
232
+ dat = self._buf.read(size)
233
+ self._buflen -= size
234
+ self._br += size
235
+ return dat
236
+ else:
237
+ dat = self._buf.read() # ouch, duplicates data
238
+ size -= self._buflen
239
+ self._br += self._buflen
240
+
241
+ self._buflen = 0
242
+ self._buf = None
243
+ # END handle buffer len
244
+ # END handle buffer
245
+
246
+ # decompress some data
247
+ # Abstract: zlib needs to operate on chunks of our memory map ( which may
248
+ # be large ), as it will otherwise and always fill in the 'unconsumed_tail'
249
+ # attribute which possible reads our whole map to the end, forcing
250
+ # everything to be read from disk even though just a portion was requested.
251
+ # As this would be a nogo, we workaround it by passing only chunks of data,
252
+ # moving the window into the memory map along as we decompress, which keeps
253
+ # the tail smaller than our chunk-size. This causes 'only' the chunk to be
254
+ # copied once, and another copy of a part of it when it creates the unconsumed
255
+ # tail. We have to use it to hand in the appropriate amount of bytes during
256
+ # the next read.
257
+ tail = self._zip.unconsumed_tail
258
+ if tail:
259
+ # move the window, make it as large as size demands. For code-clarity,
260
+ # we just take the chunk from our map again instead of reusing the unconsumed
261
+ # tail. The latter one would safe some memory copying, but we could end up
262
+ # with not getting enough data uncompressed, so we had to sort that out as well.
263
+ # Now we just assume the worst case, hence the data is uncompressed and the window
264
+ # needs to be as large as the uncompressed bytes we want to read.
265
+ self._cws = self._cwe - len(tail)
266
+ self._cwe = self._cws + size
267
+ else:
268
+ cws = self._cws
269
+ self._cws = self._cwe
270
+ self._cwe = cws + size
271
+ # END handle tail
272
+
273
+ # if window is too small, make it larger so zip can decompress something
274
+ if self._cwe - self._cws < 8:
275
+ self._cwe = self._cws + 8
276
+ # END adjust winsize
277
+
278
+ # takes a slice, but doesn't copy the data, it says ...
279
+ indata = self._m[self._cws:self._cwe]
280
+
281
+ # get the actual window end to be sure we don't use it for computations
282
+ self._cwe = self._cws + len(indata)
283
+ dcompdat = self._zip.decompress(indata, size)
284
+ # update the amount of compressed bytes read
285
+ # We feed possibly overlapping chunks, which is why the unconsumed tail
286
+ # has to be taken into consideration, as well as the unused data
287
+ # if we hit the end of the stream
288
+ # NOTE: Behavior changed in PY2.7 onward, which requires special handling to make the tests work properly.
289
+ # They are thorough, and I assume it is truly working.
290
+ # Why is this logic as convoluted as it is ? Please look at the table in
291
+ # https://github.com/gitpython-developers/gitdb/issues/19 to learn about the test-results.
292
+ # Basically, on py2.6, you want to use branch 1, whereas on all other python version, the second branch
293
+ # will be the one that works.
294
+ # However, the zlib VERSIONs as well as the platform check is used to further match the entries in the
295
+ # table in the github issue. This is it ... it was the only way I could make this work everywhere.
296
+ # IT's CERTAINLY GOING TO BITE US IN THE FUTURE ... .
297
+ if getattr(zlib, 'ZLIB_RUNTIME_VERSION', zlib.ZLIB_VERSION) in ('1.2.7', '1.2.5') and not sys.platform == 'darwin':
298
+ unused_datalen = len(self._zip.unconsumed_tail)
299
+ else:
300
+ unused_datalen = len(self._zip.unconsumed_tail) + len(self._zip.unused_data)
301
+ # # end handle very special case ...
302
+
303
+ self._cbr += len(indata) - unused_datalen
304
+ self._br += len(dcompdat)
305
+
306
+ if dat:
307
+ dcompdat = dat + dcompdat
308
+ # END prepend our cached data
309
+
310
+ # it can happen, depending on the compression, that we get less bytes
311
+ # than ordered as it needs the final portion of the data as well.
312
+ # Recursively resolve that.
313
+ # Note: dcompdat can be empty even though we still appear to have bytes
314
+ # to read, if we are called by compressed_bytes_read - it manipulates
315
+ # us to empty the stream
316
+ if dcompdat and (len(dcompdat) - len(dat)) < size and self._br < self._s:
317
+ dcompdat += self.read(size - len(dcompdat))
318
+ # END handle special case
319
+ return dcompdat
320
+
321
+
322
+ class DeltaApplyReader(LazyMixin):
323
+
324
+ """A reader which dynamically applies pack deltas to a base object, keeping the
325
+ memory demands to a minimum.
326
+
327
+ The size of the final object is only obtainable once all deltas have been
328
+ applied, unless it is retrieved from a pack index.
329
+
330
+ The uncompressed Delta has the following layout (MSB being a most significant
331
+ bit encoded dynamic size):
332
+
333
+ * MSB Source Size - the size of the base against which the delta was created
334
+ * MSB Target Size - the size of the resulting data after the delta was applied
335
+ * A list of one byte commands (cmd) which are followed by a specific protocol:
336
+
337
+ * cmd & 0x80 - copy delta_data[offset:offset+size]
338
+
339
+ * Followed by an encoded offset into the delta data
340
+ * Followed by an encoded size of the chunk to copy
341
+
342
+ * cmd & 0x7f - insert
343
+
344
+ * insert cmd bytes from the delta buffer into the output stream
345
+
346
+ * cmd == 0 - invalid operation ( or error in delta stream )
347
+ """
348
+ __slots__ = (
349
+ "_bstream", # base stream to which to apply the deltas
350
+ "_dstreams", # tuple of delta stream readers
351
+ "_mm_target", # memory map of the delta-applied data
352
+ "_size", # actual number of bytes in _mm_target
353
+ "_br" # number of bytes read
354
+ )
355
+
356
+ #{ Configuration
357
+ k_max_memory_move = 250 * 1000 * 1000
358
+ #} END configuration
359
+
360
+ def __init__(self, stream_list):
361
+ """Initialize this instance with a list of streams, the first stream being
362
+ the delta to apply on top of all following deltas, the last stream being the
363
+ base object onto which to apply the deltas"""
364
+ assert len(stream_list) > 1, "Need at least one delta and one base stream"
365
+
366
+ self._bstream = stream_list[-1]
367
+ self._dstreams = tuple(stream_list[:-1])
368
+ self._br = 0
369
+
370
+ def _set_cache_too_slow_without_c(self, attr):
371
+ # the direct algorithm is fastest and most direct if there is only one
372
+ # delta. Also, the extra overhead might not be worth it for items smaller
373
+ # than X - definitely the case in python, every function call costs
374
+ # huge amounts of time
375
+ # if len(self._dstreams) * self._bstream.size < self.k_max_memory_move:
376
+ if len(self._dstreams) == 1:
377
+ return self._set_cache_brute_(attr)
378
+
379
+ # Aggregate all deltas into one delta in reverse order. Hence we take
380
+ # the last delta, and reverse-merge its ancestor delta, until we receive
381
+ # the final delta data stream.
382
+ dcl = connect_deltas(self._dstreams)
383
+
384
+ # call len directly, as the (optional) c version doesn't implement the sequence
385
+ # protocol
386
+ if dcl.rbound() == 0:
387
+ self._size = 0
388
+ self._mm_target = allocate_memory(0)
389
+ return
390
+ # END handle empty list
391
+
392
+ self._size = dcl.rbound()
393
+ self._mm_target = allocate_memory(self._size)
394
+
395
+ bbuf = allocate_memory(self._bstream.size)
396
+ stream_copy(self._bstream.read, bbuf.write, self._bstream.size, 256 * mmap.PAGESIZE)
397
+
398
+ # APPLY CHUNKS
399
+ write = self._mm_target.write
400
+ dcl.apply(bbuf, write)
401
+
402
+ self._mm_target.seek(0)
403
+
404
+ def _set_cache_brute_(self, attr):
405
+ """If we are here, we apply the actual deltas"""
406
+ # TODO: There should be a special case if there is only one stream
407
+ # Then the default-git algorithm should perform a tad faster, as the
408
+ # delta is not peaked into, causing less overhead.
409
+ buffer_info_list = list()
410
+ max_target_size = 0
411
+ for dstream in self._dstreams:
412
+ buf = dstream.read(512) # read the header information + X
413
+ offset, src_size = msb_size(buf)
414
+ offset, target_size = msb_size(buf, offset)
415
+ buffer_info_list.append((buf[offset:], offset, src_size, target_size))
416
+ max_target_size = max(max_target_size, target_size)
417
+ # END for each delta stream
418
+
419
+ # sanity check - the first delta to apply should have the same source
420
+ # size as our actual base stream
421
+ base_size = self._bstream.size
422
+ target_size = max_target_size
423
+
424
+ # if we have more than 1 delta to apply, we will swap buffers, hence we must
425
+ # assure that all buffers we use are large enough to hold all the results
426
+ if len(self._dstreams) > 1:
427
+ base_size = target_size = max(base_size, max_target_size)
428
+ # END adjust buffer sizes
429
+
430
+ # Allocate private memory map big enough to hold the first base buffer
431
+ # We need random access to it
432
+ bbuf = allocate_memory(base_size)
433
+ stream_copy(self._bstream.read, bbuf.write, base_size, 256 * mmap.PAGESIZE)
434
+
435
+ # allocate memory map large enough for the largest (intermediate) target
436
+ # We will use it as scratch space for all delta ops. If the final
437
+ # target buffer is smaller than our allocated space, we just use parts
438
+ # of it upon return.
439
+ tbuf = allocate_memory(target_size)
440
+
441
+ # for each delta to apply, memory map the decompressed delta and
442
+ # work on the op-codes to reconstruct everything.
443
+ # For the actual copying, we use a seek and write pattern of buffer
444
+ # slices.
445
+ final_target_size = None
446
+ for (dbuf, offset, src_size, target_size), dstream in zip(reversed(buffer_info_list), reversed(self._dstreams)):
447
+ # allocate a buffer to hold all delta data - fill in the data for
448
+ # fast access. We do this as we know that reading individual bytes
449
+ # from our stream would be slower than necessary ( although possible )
450
+ # The dbuf buffer contains commands after the first two MSB sizes, the
451
+ # offset specifies the amount of bytes read to get the sizes.
452
+ ddata = allocate_memory(dstream.size - offset)
453
+ ddata.write(dbuf)
454
+ # read the rest from the stream. The size we give is larger than necessary
455
+ stream_copy(dstream.read, ddata.write, dstream.size, 256 * mmap.PAGESIZE)
456
+
457
+ #######################################################################
458
+ if 'c_apply_delta' in globals():
459
+ c_apply_delta(bbuf, ddata, tbuf)
460
+ else:
461
+ apply_delta_data(bbuf, src_size, ddata, len(ddata), tbuf.write)
462
+ #######################################################################
463
+
464
+ # finally, swap out source and target buffers. The target is now the
465
+ # base for the next delta to apply
466
+ bbuf, tbuf = tbuf, bbuf
467
+ bbuf.seek(0)
468
+ tbuf.seek(0)
469
+ final_target_size = target_size
470
+ # END for each delta to apply
471
+
472
+ # its already seeked to 0, constrain it to the actual size
473
+ # NOTE: in the end of the loop, it swaps buffers, hence our target buffer
474
+ # is not tbuf, but bbuf !
475
+ self._mm_target = bbuf
476
+ self._size = final_target_size
477
+
478
+ #{ Configuration
479
+ if not has_perf_mod:
480
+ _set_cache_ = _set_cache_brute_
481
+ else:
482
+ _set_cache_ = _set_cache_too_slow_without_c
483
+
484
+ #} END configuration
485
+
486
+ def read(self, count=0):
487
+ bl = self._size - self._br # bytes left
488
+ if count < 1 or count > bl:
489
+ count = bl
490
+ # NOTE: we could check for certain size limits, and possibly
491
+ # return buffers instead of strings to prevent byte copying
492
+ data = self._mm_target.read(count)
493
+ self._br += len(data)
494
+ return data
495
+
496
+ def seek(self, offset, whence=getattr(os, 'SEEK_SET', 0)):
497
+ """Allows to reset the stream to restart reading
498
+
499
+ :raise ValueError: If offset and whence are not 0"""
500
+ if offset != 0 or whence != getattr(os, 'SEEK_SET', 0):
501
+ raise ValueError("Can only seek to position 0")
502
+ # END handle offset
503
+ self._br = 0
504
+ self._mm_target.seek(0)
505
+
506
+ #{ Interface
507
+
508
+ @classmethod
509
+ def new(cls, stream_list):
510
+ """
511
+ Convert the given list of streams into a stream which resolves deltas
512
+ when reading from it.
513
+
514
+ :param stream_list: two or more stream objects, first stream is a Delta
515
+ to the object that you want to resolve, followed by N additional delta
516
+ streams. The list's last stream must be a non-delta stream.
517
+
518
+ :return: Non-Delta OPackStream object whose stream can be used to obtain
519
+ the decompressed resolved data
520
+ :raise ValueError: if the stream list cannot be handled"""
521
+ if len(stream_list) < 2:
522
+ raise ValueError("Need at least two streams")
523
+ # END single object special handling
524
+
525
+ if stream_list[-1].type_id in delta_types:
526
+ raise ValueError(
527
+ "Cannot resolve deltas if there is no base object stream, last one was type: %s" % stream_list[-1].type)
528
+ # END check stream
529
+ return cls(stream_list)
530
+
531
+ #} END interface
532
+
533
+ #{ OInfo like Interface
534
+
535
+ @property
536
+ def type(self):
537
+ return self._bstream.type
538
+
539
+ @property
540
+ def type_id(self):
541
+ return self._bstream.type_id
542
+
543
+ @property
544
+ def size(self):
545
+ """:return: number of uncompressed bytes in the stream"""
546
+ return self._size
547
+
548
+ #} END oinfo like interface
549
+
550
+
551
+ #} END RO streams
552
+
553
+
554
+ #{ W Streams
555
+
556
+ class Sha1Writer:
557
+
558
+ """Simple stream writer which produces a sha whenever you like as it degests
559
+ everything it is supposed to write"""
560
+ __slots__ = "sha1"
561
+
562
+ def __init__(self):
563
+ self.sha1 = make_sha()
564
+
565
+ #{ Stream Interface
566
+
567
+ def write(self, data):
568
+ """:raise IOError: If not all bytes could be written
569
+ :param data: byte object
570
+ :return: length of incoming data"""
571
+
572
+ self.sha1.update(data)
573
+
574
+ return len(data)
575
+
576
+ # END stream interface
577
+
578
+ #{ Interface
579
+
580
+ def sha(self, as_hex=False):
581
+ """:return: sha so far
582
+ :param as_hex: if True, sha will be hex-encoded, binary otherwise"""
583
+ if as_hex:
584
+ return self.sha1.hexdigest()
585
+ return self.sha1.digest()
586
+
587
+ #} END interface
588
+
589
+
590
+ class FlexibleSha1Writer(Sha1Writer):
591
+
592
+ """Writer producing a sha1 while passing on the written bytes to the given
593
+ write function"""
594
+ __slots__ = 'writer'
595
+
596
+ def __init__(self, writer):
597
+ Sha1Writer.__init__(self)
598
+ self.writer = writer
599
+
600
+ def write(self, data):
601
+ Sha1Writer.write(self, data)
602
+ self.writer(data)
603
+
604
+
605
+ class ZippedStoreShaWriter(Sha1Writer):
606
+
607
+ """Remembers everything someone writes to it and generates a sha"""
608
+ __slots__ = ('buf', 'zip')
609
+
610
+ def __init__(self):
611
+ Sha1Writer.__init__(self)
612
+ self.buf = BytesIO()
613
+ self.zip = zlib.compressobj(zlib.Z_BEST_SPEED)
614
+
615
+ def __getattr__(self, attr):
616
+ return getattr(self.buf, attr)
617
+
618
+ def write(self, data):
619
+ alen = Sha1Writer.write(self, data)
620
+ self.buf.write(self.zip.compress(data))
621
+
622
+ return alen
623
+
624
+ def close(self):
625
+ self.buf.write(self.zip.flush())
626
+
627
+ def seek(self, offset, whence=getattr(os, 'SEEK_SET', 0)):
628
+ """Seeking currently only supports to rewind written data
629
+ Multiple writes are not supported"""
630
+ if offset != 0 or whence != getattr(os, 'SEEK_SET', 0):
631
+ raise ValueError("Can only seek to position 0")
632
+ # END handle offset
633
+ self.buf.seek(0)
634
+
635
+ def getvalue(self):
636
+ """:return: string value from the current stream position to the end"""
637
+ return self.buf.getvalue()
638
+
639
+
640
+ class FDCompressedSha1Writer(Sha1Writer):
641
+
642
+ """Digests data written to it, making the sha available, then compress the
643
+ data and write it to the file descriptor
644
+
645
+ **Note:** operates on raw file descriptors
646
+ **Note:** for this to work, you have to use the close-method of this instance"""
647
+ __slots__ = ("fd", "sha1", "zip")
648
+
649
+ # default exception
650
+ exc = IOError("Failed to write all bytes to filedescriptor")
651
+
652
+ def __init__(self, fd):
653
+ super().__init__()
654
+ self.fd = fd
655
+ self.zip = zlib.compressobj(zlib.Z_BEST_SPEED)
656
+
657
+ #{ Stream Interface
658
+
659
+ def write(self, data):
660
+ """:raise IOError: If not all bytes could be written
661
+ :return: length of incoming data"""
662
+ self.sha1.update(data)
663
+ cdata = self.zip.compress(data)
664
+ bytes_written = write(self.fd, cdata)
665
+
666
+ if bytes_written != len(cdata):
667
+ raise self.exc
668
+
669
+ return len(data)
670
+
671
+ def close(self):
672
+ remainder = self.zip.flush()
673
+ if write(self.fd, remainder) != len(remainder):
674
+ raise self.exc
675
+ return close(self.fd)
676
+
677
+ #} END stream interface
678
+
679
+
680
+ class FDStream:
681
+
682
+ """A simple wrapper providing the most basic functions on a file descriptor
683
+ with the fileobject interface. Cannot use os.fdopen as the resulting stream
684
+ takes ownership"""
685
+ __slots__ = ("_fd", '_pos')
686
+
687
+ def __init__(self, fd):
688
+ self._fd = fd
689
+ self._pos = 0
690
+
691
+ def write(self, data):
692
+ self._pos += len(data)
693
+ os.write(self._fd, data)
694
+
695
+ def read(self, count=0):
696
+ if count == 0:
697
+ count = os.path.getsize(self._filepath)
698
+ # END handle read everything
699
+
700
+ bytes = os.read(self._fd, count)
701
+ self._pos += len(bytes)
702
+ return bytes
703
+
704
+ def fileno(self):
705
+ return self._fd
706
+
707
+ def tell(self):
708
+ return self._pos
709
+
710
+ def close(self):
711
+ close(self._fd)
712
+
713
+
714
+ class NullStream:
715
+
716
+ """A stream that does nothing but providing a stream interface.
717
+ Use it like /dev/null"""
718
+ __slots__ = tuple()
719
+
720
+ def read(self, size=0):
721
+ return ''
722
+
723
+ def close(self):
724
+ pass
725
+
726
+ def write(self, data):
727
+ return len(data)
728
+
729
+
730
+ #} END W streams
lib/python3.12/site-packages/gitdb/test/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
lib/python3.12/site-packages/gitdb/test/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (189 Bytes). View file
 
lib/python3.12/site-packages/gitdb/test/__pycache__/lib.cpython-312.pyc ADDED
Binary file (8.11 kB). View file
 
lib/python3.12/site-packages/gitdb/test/__pycache__/test_base.cpython-312.pyc ADDED
Binary file (3.83 kB). View file
 
lib/python3.12/site-packages/gitdb/test/__pycache__/test_example.cpython-312.pyc ADDED
Binary file (1.93 kB). View file
 
lib/python3.12/site-packages/gitdb/test/__pycache__/test_pack.cpython-312.pyc ADDED
Binary file (11.3 kB). View file
 
lib/python3.12/site-packages/gitdb/test/__pycache__/test_stream.cpython-312.pyc ADDED
Binary file (7.01 kB). View file
 
lib/python3.12/site-packages/gitdb/test/__pycache__/test_util.cpython-312.pyc ADDED
Binary file (4.67 kB). View file
 
lib/python3.12/site-packages/gitdb/test/lib.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
2
+ #
3
+ # This module is part of GitDB and is released under
4
+ # the New BSD License: https://opensource.org/license/bsd-3-clause/
5
+ """Utilities used in ODB testing"""
6
+ from gitdb import OStream
7
+
8
+ import sys
9
+ import random
10
+ from array import array
11
+
12
+ from io import BytesIO
13
+
14
+ import glob
15
+ import unittest
16
+ import tempfile
17
+ import shutil
18
+ import os
19
+ import gc
20
+ import logging
21
+ from functools import wraps
22
+
23
+
24
+ #{ Bases
25
+
26
+ class TestBase(unittest.TestCase):
27
+ """Base class for all tests
28
+
29
+ TestCase providing access to readonly repositories using the following member variables.
30
+
31
+ * gitrepopath
32
+
33
+ * read-only base path of the git source repository, i.e. .../git/.git
34
+ """
35
+
36
+ #{ Invvariants
37
+ k_env_git_repo = "GITDB_TEST_GIT_REPO_BASE"
38
+ #} END invariants
39
+
40
+ @classmethod
41
+ def setUpClass(cls):
42
+ try:
43
+ super().setUpClass()
44
+ except AttributeError:
45
+ pass
46
+
47
+ cls.gitrepopath = os.environ.get(cls.k_env_git_repo)
48
+ if not cls.gitrepopath:
49
+ logging.info(
50
+ "You can set the %s environment variable to a .git repository of your choice - defaulting to the gitdb repository", cls.k_env_git_repo)
51
+ ospd = os.path.dirname
52
+ cls.gitrepopath = os.path.join(ospd(ospd(ospd(__file__))), '.git')
53
+ # end assure gitrepo is set
54
+ assert cls.gitrepopath.endswith('.git')
55
+
56
+
57
+ #} END bases
58
+
59
+ #{ Decorators
60
+
61
+ def with_rw_directory(func):
62
+ """Create a temporary directory which can be written to, remove it if the
63
+ test succeeds, but leave it otherwise to aid additional debugging"""
64
+
65
+ def wrapper(self):
66
+ path = tempfile.mktemp(prefix=func.__name__)
67
+ os.mkdir(path)
68
+ keep = False
69
+ try:
70
+ try:
71
+ return func(self, path)
72
+ except Exception:
73
+ sys.stderr.write(f"Test {type(self).__name__}.{func.__name__} failed, output is at {path!r}\n")
74
+ keep = True
75
+ raise
76
+ finally:
77
+ # Need to collect here to be sure all handles have been closed. It appears
78
+ # a windows-only issue. In fact things should be deleted, as well as
79
+ # memory maps closed, once objects go out of scope. For some reason
80
+ # though this is not the case here unless we collect explicitly.
81
+ if not keep:
82
+ gc.collect()
83
+ shutil.rmtree(path)
84
+ # END handle exception
85
+ # END wrapper
86
+
87
+ wrapper.__name__ = func.__name__
88
+ return wrapper
89
+
90
+
91
+ def with_packs_rw(func):
92
+ """Function that provides a path into which the packs for testing should be
93
+ copied. Will pass on the path to the actual function afterwards"""
94
+
95
+ def wrapper(self, path):
96
+ src_pack_glob = fixture_path('packs/*')
97
+ copy_files_globbed(src_pack_glob, path, hard_link_ok=True)
98
+ return func(self, path)
99
+ # END wrapper
100
+
101
+ wrapper.__name__ = func.__name__
102
+ return wrapper
103
+
104
+ #} END decorators
105
+
106
+ #{ Routines
107
+
108
+
109
+ def fixture_path(relapath=''):
110
+ """:return: absolute path into the fixture directory
111
+ :param relapath: relative path into the fixtures directory, or ''
112
+ to obtain the fixture directory itself"""
113
+ return os.path.join(os.path.dirname(__file__), 'fixtures', relapath)
114
+
115
+
116
+ def copy_files_globbed(source_glob, target_dir, hard_link_ok=False):
117
+ """Copy all files found according to the given source glob into the target directory
118
+ :param hard_link_ok: if True, hard links will be created if possible. Otherwise
119
+ the files will be copied"""
120
+ for src_file in glob.glob(source_glob):
121
+ if hard_link_ok and hasattr(os, 'link'):
122
+ target = os.path.join(target_dir, os.path.basename(src_file))
123
+ try:
124
+ os.link(src_file, target)
125
+ except OSError:
126
+ shutil.copy(src_file, target_dir)
127
+ # END handle cross device links ( and resulting failure )
128
+ else:
129
+ shutil.copy(src_file, target_dir)
130
+ # END try hard link
131
+ # END for each file to copy
132
+
133
+
134
+ def make_bytes(size_in_bytes, randomize=False):
135
+ """:return: string with given size in bytes
136
+ :param randomize: try to produce a very random stream"""
137
+ actual_size = size_in_bytes // 4
138
+ producer = range(actual_size)
139
+ if randomize:
140
+ producer = list(producer)
141
+ random.shuffle(producer)
142
+ # END randomize
143
+ a = array('i', producer)
144
+ return a.tobytes()
145
+
146
+
147
+ def make_object(type, data):
148
+ """:return: bytes resembling an uncompressed object"""
149
+ odata = "blob %i\0" % len(data)
150
+ return odata.encode("ascii") + data
151
+
152
+
153
+ def make_memory_file(size_in_bytes, randomize=False):
154
+ """:return: tuple(size_of_stream, stream)
155
+ :param randomize: try to produce a very random stream"""
156
+ d = make_bytes(size_in_bytes, randomize)
157
+ return len(d), BytesIO(d)
158
+
159
+ #} END routines
160
+
161
+ #{ Stream Utilities
162
+
163
+
164
+ class DummyStream:
165
+
166
+ def __init__(self):
167
+ self.was_read = False
168
+ self.bytes = 0
169
+ self.closed = False
170
+
171
+ def read(self, size):
172
+ self.was_read = True
173
+ self.bytes = size
174
+
175
+ def close(self):
176
+ self.closed = True
177
+
178
+ def _assert(self):
179
+ assert self.was_read
180
+
181
+
182
+ class DeriveTest(OStream):
183
+
184
+ def __init__(self, sha, type, size, stream, *args, **kwargs):
185
+ self.myarg = kwargs.pop('myarg')
186
+ self.args = args
187
+
188
+ def _assert(self):
189
+ assert self.args
190
+ assert self.myarg
191
+
192
+ #} END stream utilitiess