Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/INSTALLER +1 -0
- lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE +279 -0
- lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA +123 -0
- lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/RECORD +16 -0
- lib/python3.12/site-packages/aiohappyeyeballs-2.6.1.dist-info/WHEEL +4 -0
- lib/python3.12/site-packages/fonttools-4.61.1.dist-info/RECORD +689 -0
- lib/python3.12/site-packages/fonttools-4.61.1.dist-info/WHEEL +8 -0
- lib/python3.12/site-packages/fonttools-4.61.1.dist-info/entry_points.txt +5 -0
- lib/python3.12/site-packages/fonttools-4.61.1.dist-info/licenses/LICENSE +21 -0
- lib/python3.12/site-packages/fonttools-4.61.1.dist-info/licenses/LICENSE.external +388 -0
- lib/python3.12/site-packages/fonttools-4.61.1.dist-info/top_level.txt +1 -0
- lib/python3.12/site-packages/gitdb/__init__.py +16 -0
- lib/python3.12/site-packages/gitdb/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/__pycache__/base.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/__pycache__/const.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/__pycache__/exc.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/__pycache__/fun.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/__pycache__/pack.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/__pycache__/stream.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/__pycache__/typ.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/__pycache__/util.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/base.py +315 -0
- lib/python3.12/site-packages/gitdb/const.py +4 -0
- lib/python3.12/site-packages/gitdb/db/__init__.py +11 -0
- lib/python3.12/site-packages/gitdb/db/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/db/__pycache__/base.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/db/__pycache__/git.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/db/__pycache__/loose.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/db/__pycache__/mem.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/db/__pycache__/pack.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/db/__pycache__/ref.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/db/base.py +278 -0
- lib/python3.12/site-packages/gitdb/db/git.py +85 -0
- lib/python3.12/site-packages/gitdb/db/loose.py +268 -0
- lib/python3.12/site-packages/gitdb/db/mem.py +110 -0
- lib/python3.12/site-packages/gitdb/db/pack.py +206 -0
- lib/python3.12/site-packages/gitdb/db/ref.py +82 -0
- lib/python3.12/site-packages/gitdb/exc.py +57 -0
- lib/python3.12/site-packages/gitdb/fun.py +704 -0
- lib/python3.12/site-packages/gitdb/pack.py +1031 -0
- lib/python3.12/site-packages/gitdb/stream.py +730 -0
- lib/python3.12/site-packages/gitdb/test/__init__.py +4 -0
- lib/python3.12/site-packages/gitdb/test/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/test/__pycache__/lib.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/test/__pycache__/test_base.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/test/__pycache__/test_example.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/test/__pycache__/test_pack.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/test/__pycache__/test_stream.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/gitdb/test/__pycache__/test_util.cpython-312.pyc +0 -0
- 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&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
|