pip 버전 업데이트 및 인코딩 방법 수정정

This commit is contained in:
3minbe 2025-02-20 13:16:49 +09:00
parent 02df1b0ba7
commit e27abe2a32
115 changed files with 13520 additions and 14586 deletions

View File

@ -555,7 +555,7 @@ class MainView(QtWidgets.QMainWindow):
self.updateAlertText(f"변환 성공", [file_path])
except subprocess.CalledProcessError as e:
self.updateAlertText(f"변환 실패: {e}", [file_path])
self.updateAlertText(f"변환 실패\n {e}", [file_path])
self.progress_bar.setValue(int((index + 1) / total_files * 100))
self.status_bar.showMessage("변환 완료")
self.progress_bar.setVisible(False)

View File

@ -1,67 +1,75 @@
import re, sys
import re
import sys
def load_dbc_file(file_path):
try:
with open(file_path, 'r') as file:
content = file.read()
# print(f"[INFO] Successfully loaded DBC file: {file_path}")
encodings = ['utf-8', 'cp949', 'latin1'] # 시도할 인코딩 목록
# 메시지와 시그널을 추출하는 정규 표현식
message_pattern = re.compile(r'BO_\s+(\d+)\s+(\w+)\s*:\s*(\d+)\s+(\w+)')
signal_pattern = re.compile(r'SG_\s+(\w+)\s*:\s*(\d+)\|(\d+)@(\d+)([+-])\s*\(([^,]+),\s*([^)]+)\)\s*\[[^\]]+\]\s*\"[^\"]*\"\s+(\w+)')
for encoding in encodings:
try:
with open(file_path, 'r', encoding=encoding) as file:
content = file.read()
print(f"[INFO] Successfully loaded DBC file with encoding: {encoding}")
break # 파일을 성공적으로 읽으면 루프를 종료
except (UnicodeDecodeError, FileNotFoundError) as e:
print(f"[WARNING] Failed to read file with encoding {encoding}: {e}")
content = None
# 메시지와 시그널 매칭
messages = {}
current_message = None
for line in content.splitlines():
line = line.strip()
if not line:
current_message = None
continue
message_match = message_pattern.match(line)
if message_match:
decimal_id = int(message_match.group(1))
hex_id = f"0x{decimal_id:03X}"
message_name = message_match.group(2)
dlc = int(message_match.group(3))
tx_ecu_name = message_match.group(4)
current_message = message_name
messages[current_message] = {
"ID": hex_id,
"DLC": dlc,
"TX ECU name": tx_ecu_name,
"Signals": []
}
elif current_message:
signal_match = signal_pattern.match(line)
if signal_match:
signal_name = signal_match.group(1)
msb = int(signal_match.group(2))
length = int(signal_match.group(3))
byte_order = int(signal_match.group(4))
sign = signal_match.group(5)
factor = float(signal_match.group(6))
offset = float(signal_match.group(7))
rx_ecu_name = signal_match.group(8)
messages[current_message]["Signals"].append({
"Signal name": signal_name,
"msb": msb,
"Length": length,
"Byte order": byte_order,
"Sign": sign,
"Factor": factor,
"Offset": offset,
"RX ECU name": rx_ecu_name
})
return messages
except Exception as e:
print(f"[ERROR] Failed to parse DBC file: {e}")
if content is None:
print(f"[ERROR] Failed to load DBC file with all attempted encodings.")
return None
# 메시지와 시그널을 추출하는 정규 표현식
message_pattern = re.compile(r'BO_\s+(\d+)\s+(\w+)\s*:\s*(\d+)\s+(\w+)')
signal_pattern = re.compile(r'SG_\s+(\w+)\s*:\s*(\d+)\|(\d+)@(\d+)([+-])\s*\(([^,]+),\s*([^)]+)\)\s*\[[^\]]+\]\s*\"[^\"]*\"\s+(\w+)')
# 메시지와 시그널 매칭
messages = {}
current_message = None
for line in content.splitlines():
line = line.strip()
if not line:
current_message = None
continue
message_match = message_pattern.match(line)
if message_match:
decimal_id = int(message_match.group(1))
hex_id = f"0x{decimal_id:03X}"
message_name = message_match.group(2)
dlc = int(message_match.group(3))
tx_ecu_name = message_match.group(4)
current_message = message_name
messages[current_message] = {
"ID": hex_id,
"DLC": dlc,
"TX ECU name": tx_ecu_name,
"Signals": []
}
elif current_message:
signal_match = signal_pattern.match(line)
if signal_match:
signal_name = signal_match.group(1)
msb = int(signal_match.group(2))
length = int(signal_match.group(3))
byte_order = int(signal_match.group(4))
sign = signal_match.group(5)
factor = float(signal_match.group(6))
offset = float(signal_match.group(7))
rx_ecu_name = signal_match.group(8)
messages[current_message]["Signals"].append({
"Signal name": signal_name,
"msb": msb,
"Length": length,
"Byte order": byte_order,
"Sign": sign,
"Factor": factor,
"Offset": offset,
"RX ECU name": rx_ecu_name
})
return messages
if __name__ == "__main__":
file_path = sys.argv[1]
messages = load_dbc_file(file_path)

Binary file not shown.

View File

@ -1,799 +0,0 @@
@Switch01
A_Rog
Aakanksha Agrawal
Abhinav Sagar
ABHYUDAY PRATAP SINGH
abs51295
AceGentile
Adam Chainz
Adam Tse
Adam Wentz
admin
Adolfo Ochagavía
Adrien Morison
Agus
ahayrapetyan
Ahilya
AinsworthK
Akash Srivastava
Alan Yee
Albert Tugushev
Albert-Guan
albertg
Alberto Sottile
Aleks Bunin
Ales Erjavec
Alethea Flowers
Alex Gaynor
Alex Grönholm
Alex Hedges
Alex Loosley
Alex Morega
Alex Stachowiak
Alexander Shtyrov
Alexandre Conrad
Alexey Popravka
Aleš Erjavec
Alli
Ami Fischman
Ananya Maiti
Anatoly Techtonik
Anders Kaseorg
Andre Aguiar
Andreas Lutro
Andrei Geacar
Andrew Gaul
Andrew Shymanel
Andrey Bienkowski
Andrey Bulgakov
Andrés Delfino
Andy Freeland
Andy Kluger
Ani Hayrapetyan
Aniruddha Basak
Anish Tambe
Anrs Hu
Anthony Sottile
Antoine Musso
Anton Ovchinnikov
Anton Patrushev
Anton Zelenov
Antonio Alvarado Hernandez
Antony Lee
Antti Kaihola
Anubhav Patel
Anudit Nagar
Anuj Godase
AQNOUCH Mohammed
AraHaan
arena
arenasys
Arindam Choudhury
Armin Ronacher
Arnon Yaari
Artem
Arun Babu Neelicattu
Ashley Manton
Ashwin Ramaswami
atse
Atsushi Odagiri
Avinash Karhana
Avner Cohen
Awit (Ah-Wit) Ghirmai
Baptiste Mispelon
Barney Gale
barneygale
Bartek Ogryczak
Bastian Venthur
Ben Bodenmiller
Ben Darnell
Ben Hoyt
Ben Mares
Ben Rosser
Bence Nagy
Benjamin Peterson
Benjamin VanEvery
Benoit Pierre
Berker Peksag
Bernard
Bernard Tyers
Bernardo B. Marques
Bernhard M. Wiedemann
Bertil Hatt
Bhavam Vidyarthi
Blazej Michalik
Bogdan Opanchuk
BorisZZZ
Brad Erickson
Bradley Ayers
Branch Vincent
Brandon L. Reiss
Brandt Bucher
Brannon Dorsey
Brett Randall
Brett Rosen
Brian Cristante
Brian Rosner
briantracy
BrownTruck
Bruno Oliveira
Bruno Renié
Bruno S
Bstrdsmkr
Buck Golemon
burrows
Bussonnier Matthias
bwoodsend
c22
Caleb Martinez
Calvin Smith
Carl Meyer
Carlos Liam
Carol Willing
Carter Thayer
Cass
Chandrasekhar Atina
Charlie Marsh
Chih-Hsuan Yen
Chris Brinker
Chris Hunt
Chris Jerdonek
Chris Kuehl
Chris Markiewicz
Chris McDonough
Chris Pawley
Chris Pryer
Chris Wolfe
Christian Clauss
Christian Heimes
Christian Oudard
Christoph Reiter
Christopher Hunt
Christopher Snyder
chrysle
cjc7373
Clark Boylan
Claudio Jolowicz
Clay McClure
Cody
Cody Soyland
Colin Watson
Collin Anderson
Connor Osborn
Cooper Lees
Cooper Ry Lees
Cory Benfield
Cory Wright
Craig Kerstiens
Cristian Sorinel
Cristina
Cristina Muñoz
ctg123
Curtis Doty
cytolentino
Daan De Meyer
Dale
Damian
Damian Quiroga
Damian Shaw
Dan Black
Dan Savilonis
Dan Sully
Dane Hillard
daniel
Daniel Collins
Daniel Hahler
Daniel Holth
Daniel Jost
Daniel Katz
Daniel Shaulov
Daniele Esposti
Daniele Nicolodi
Daniele Procida
Daniil Konovalenko
Danny Hermes
Danny McClanahan
Darren Kavanagh
Dav Clark
Dave Abrahams
Dave Jones
David Aguilar
David Black
David Bordeynik
David Caro
David D Lowe
David Evans
David Hewitt
David Linke
David Poggi
David Poznik
David Pursehouse
David Runge
David Tucker
David Wales
Davidovich
ddelange
Deepak Sharma
Deepyaman Datta
Denise Yu
dependabot[bot]
derwolfe
Desetude
Devesh Kumar Singh
devsagul
Diego Caraballo
Diego Ramirez
DiegoCaraballo
Dimitri Merejkowsky
Dimitri Papadopoulos
Dimitri Papadopoulos Orfanos
Dirk Stolle
Dmitry Gladkov
Dmitry Volodin
Domen Kožar
Dominic Davis-Foster
Donald Stufft
Dongweiming
doron zarhi
Dos Moonen
Douglas Thor
DrFeathers
Dustin Ingram
Dustin Rodrigues
Dwayne Bailey
Ed Morley
Edgar Ramírez
Edgar Ramírez Mondragón
Ee Durbin
Efflam Lemaillet
efflamlemaillet
Eitan Adler
ekristina
elainechan
Eli Schwartz
Elisha Hollander
Ellen Marie Dash
Emil Burzo
Emil Styrke
Emmanuel Arias
Endoh Takanao
enoch
Erdinc Mutlu
Eric Cousineau
Eric Gillingham
Eric Hanchrow
Eric Hopper
Erik M. Bray
Erik Rose
Erwin Janssen
Eugene Vereshchagin
everdimension
Federico
Felipe Peter
Felix Yan
fiber-space
Filip Kokosiński
Filipe Laíns
Finn Womack
finnagin
Flavio Amurrio
Florian Briand
Florian Rathgeber
Francesco
Francesco Montesano
Fredrik Orderud
Frost Ming
Gabriel Curio
Gabriel de Perthuis
Garry Polley
gavin
gdanielson
Geoffrey Sneddon
George Song
Georgi Valkov
Georgy Pchelkin
ghost
Giftlin Rajaiah
gizmoguy1
gkdoc
Godefroid Chapelle
Gopinath M
GOTO Hayato
gousaiyang
gpiks
Greg Roodt
Greg Ward
Guilherme Espada
Guillaume Seguin
gutsytechster
Guy Rozendorn
Guy Tuval
gzpan123
Hanjun Kim
Hari Charan
Harsh Vardhan
harupy
Harutaka Kawamura
hauntsaninja
Henrich Hartzer
Henry Schreiner
Herbert Pfennig
Holly Stotelmyer
Honnix
Hsiaoming Yang
Hugo Lopes Tavares
Hugo van Kemenade
Hugues Bruant
Hynek Schlawack
Ian Bicking
Ian Cordasco
Ian Lee
Ian Stapleton Cordasco
Ian Wienand
Igor Kuzmitshov
Igor Sobreira
Ikko Ashimine
Ilan Schnell
Illia Volochii
Ilya Baryshev
Inada Naoki
Ionel Cristian Mărieș
Ionel Maries Cristian
Itamar Turner-Trauring
Ivan Pozdeev
J. Nick Koston
Jacob Kim
Jacob Walls
Jaime Sanz
jakirkham
Jakub Kuczys
Jakub Stasiak
Jakub Vysoky
Jakub Wilk
James Cleveland
James Curtin
James Firth
James Gerity
James Polley
Jan Pokorný
Jannis Leidel
Jarek Potiuk
jarondl
Jason Curtis
Jason R. Coombs
JasonMo
JasonMo1
Jay Graves
Jean Abou Samra
Jean-Christophe Fillion-Robin
Jeff Barber
Jeff Dairiki
Jeff Widman
Jelmer Vernooij
jenix21
Jeremy Fleischman
Jeremy Stanley
Jeremy Zafran
Jesse Rittner
Jiashuo Li
Jim Fisher
Jim Garrison
Jinzhe Zeng
Jiun Bae
Jivan Amara
Joe Bylund
Joe Michelini
John Paton
John Sirois
John T. Wodder II
John-Scott Atlakson
johnthagen
Jon Banafato
Jon Dufresne
Jon Parise
Jonas Nockert
Jonathan Herbert
Joonatan Partanen
Joost Molenaar
Jorge Niedbalski
Joseph Bylund
Joseph Long
Josh Bronson
Josh Cannon
Josh Hansen
Josh Schneier
Joshua
Juan Luis Cano Rodríguez
Juanjo Bazán
Judah Rand
Julian Berman
Julian Gethmann
Julien Demoor
Jussi Kukkonen
jwg4
Jyrki Pulliainen
Kai Chen
Kai Mueller
Kamal Bin Mustafa
kasium
kaustav haldar
keanemind
Keith Maxwell
Kelsey Hightower
Kenneth Belitzky
Kenneth Reitz
Kevin Burke
Kevin Carter
Kevin Frommelt
Kevin R Patterson
Kexuan Sun
Kit Randel
Klaas van Schelven
KOLANICH
konstin
kpinc
Krishna Oza
Kumar McMillan
Kuntal Majumder
Kurt McKee
Kyle Persohn
lakshmanaram
Laszlo Kiss-Kollar
Laurent Bristiel
Laurent LAPORTE
Laurie O
Laurie Opperman
layday
Leon Sasson
Lev Givon
Lincoln de Sousa
Lipis
lorddavidiii
Loren Carvalho
Lucas Cimon
Ludovic Gasc
Luis Medel
Lukas Geiger
Lukas Juhrich
Luke Macken
Luo Jiebin
luojiebin
luz.paz
László Kiss Kollár
M00nL1ght
Marc Abramowitz
Marc Tamlyn
Marcus Smith
Mariatta
Mark Kohler
Mark McLoughlin
Mark Williams
Markus Hametner
Martey Dodoo
Martin Fischer
Martin Häcker
Martin Pavlasek
Masaki
Masklinn
Matej Stuchlik
Mathew Jennings
Mathieu Bridon
Mathieu Kniewallner
Matt Bacchi
Matt Good
Matt Maker
Matt Robenolt
Matt Wozniski
matthew
Matthew Einhorn
Matthew Feickert
Matthew Gilliard
Matthew Hughes
Matthew Iversen
Matthew Treinish
Matthew Trumbell
Matthew Willson
Matthias Bussonnier
mattip
Maurits van Rees
Max W Chase
Maxim Kurnikov
Maxime Rouyrre
mayeut
mbaluna
mdebi
memoselyk
meowmeowcat
Michael
Michael Aquilina
Michael E. Karpeles
Michael Klich
Michael Mintz
Michael Williamson
michaelpacer
Michał Górny
Mickaël Schoentgen
Miguel Araujo Perez
Mihir Singh
Mike
Mike Hendricks
Min RK
MinRK
Miro Hrončok
Monica Baluna
montefra
Monty Taylor
morotti
mrKazzila
Muha Ajjan
Nadav Wexler
Nahuel Ambrosini
Nate Coraor
Nate Prewitt
Nathan Houghton
Nathaniel J. Smith
Nehal J Wani
Neil Botelho
Nguyễn Gia Phong
Nicholas Serra
Nick Coghlan
Nick Stenning
Nick Timkovich
Nicolas Bock
Nicole Harris
Nikhil Benesch
Nikhil Ladha
Nikita Chepanov
Nikolay Korolev
Nipunn Koorapati
Nitesh Sharma
Niyas Sait
Noah
Noah Gorny
Nowell Strite
NtaleGrey
nvdv
OBITORASU
Ofek Lev
ofrinevo
Oliver Freund
Oliver Jeeves
Oliver Mannion
Oliver Tonnhofer
Olivier Girardot
Olivier Grisel
Ollie Rutherfurd
OMOTO Kenji
Omry Yadan
onlinejudge95
Oren Held
Oscar Benjamin
Oz N Tiram
Pachwenko
Patrick Dubroy
Patrick Jenkins
Patrick Lawson
patricktokeeffe
Patrik Kopkan
Paul Ganssle
Paul Kehrer
Paul Moore
Paul Nasrat
Paul Oswald
Paul van der Linden
Paulus Schoutsen
Pavel Safronov
Pavithra Eswaramoorthy
Pawel Jasinski
Paweł Szramowski
Pekka Klärck
Peter Gessler
Peter Lisák
Peter Shen
Peter Waller
Petr Viktorin
petr-tik
Phaneendra Chiruvella
Phil Elson
Phil Freo
Phil Pennock
Phil Whelan
Philip Jägenstedt
Philip Molloy
Philippe Ombredanne
Pi Delport
Pierre-Yves Rofes
Pieter Degroote
pip
Prabakaran Kumaresshan
Prabhjyotsing Surjit Singh Sodhi
Prabhu Marappan
Pradyun Gedam
Prashant Sharma
Pratik Mallya
pre-commit-ci[bot]
Preet Thakkar
Preston Holmes
Przemek Wrzos
Pulkit Goyal
q0w
Qiangning Hong
Qiming Xu
Quentin Lee
Quentin Pradet
R. David Murray
Rafael Caricio
Ralf Schmitt
Ran Benita
Razzi Abuissa
rdb
Reece Dunham
Remi Rampin
Rene Dudfield
Riccardo Magliocchetti
Riccardo Schirone
Richard Jones
Richard Si
Ricky Ng-Adam
Rishi
rmorotti
RobberPhex
Robert Collins
Robert McGibbon
Robert Pollak
Robert T. McGibbon
robin elisha robinson
Roey Berman
Rohan Jain
Roman Bogorodskiy
Roman Donchenko
Romuald Brunet
ronaudinho
Ronny Pfannschmidt
Rory McCann
Ross Brattain
Roy Wellington Ⅳ
Ruairidh MacLeod
Russell Keith-Magee
Ryan Shepherd
Ryan Wooden
ryneeverett
S. Guliaev
Sachi King
Salvatore Rinchiera
sandeepkiran-js
Sander Van Balen
Savio Jomton
schlamar
Scott Kitterman
Sean
seanj
Sebastian Jordan
Sebastian Schaetz
Segev Finer
SeongSoo Cho
Sergey Vasilyev
Seth Michael Larson
Seth Woodworth
Shahar Epstein
Shantanu
shenxianpeng
shireenrao
Shivansh-007
Shixian Sheng
Shlomi Fish
Shovan Maity
Simeon Visser
Simon Cross
Simon Pichugin
sinoroc
sinscary
snook92
socketubs
Sorin Sbarnea
Srinivas Nyayapati
Srishti Hegde
Stavros Korokithakis
Stefan Scherfke
Stefano Rivera
Stephan Erb
Stephen Rosen
stepshal
Steve (Gadget) Barnes
Steve Barnes
Steve Dower
Steve Kowalik
Steven Myint
Steven Silvester
stonebig
studioj
Stéphane Bidoul
Stéphane Bidoul (ACSONE)
Stéphane Klein
Sumana Harihareswara
Surbhi Sharma
Sviatoslav Sydorenko
Sviatoslav Sydorenko (Святослав Сидоренко)
Swat009
Sylvain
Takayuki SHIMIZUKAWA
Taneli Hukkinen
tbeswick
Thiago
Thijs Triemstra
Thomas Fenzl
Thomas Grainger
Thomas Guettler
Thomas Johansson
Thomas Kluyver
Thomas Smith
Thomas VINCENT
Tim D. Smith
Tim Gates
Tim Harder
Tim Heap
tim smith
tinruufu
Tobias Hermann
Tom Forbes
Tom Freudenheim
Tom V
Tomas Hrnciar
Tomas Orsava
Tomer Chachamu
Tommi Enenkel | AnB
Tomáš Hrnčiar
Tony Beswick
Tony Narlock
Tony Zhaocheng Tan
TonyBeswick
toonarmycaptain
Toshio Kuratomi
toxinu
Travis Swicegood
Tushar Sadhwani
Tzu-ping Chung
Valentin Haenel
Victor Stinner
victorvpaulo
Vikram - Google
Viktor Szépe
Ville Skyttä
Vinay Sajip
Vincent Philippon
Vinicyus Macedo
Vipul Kumar
Vitaly Babiy
Vladimir Fokow
Vladimir Rutsky
W. Trevor King
Wil Tan
Wilfred Hughes
William Edwards
William ML Leslie
William T Olson
William Woodruff
Wilson Mo
wim glenn
Winson Luk
Wolfgang Maier
Wu Zhenyu
XAMES3
Xavier Fernandez
Xianpeng Shen
xoviat
xtreak
YAMAMOTO Takashi
Yen Chi Hsuan
Yeray Diaz Diaz
Yoval P
Yu Jian
Yuan Jing Vincent Yan
Yusuke Hayashi
Zearin
Zhiping Deng
ziebam
Zvezdan Petkovic
Łukasz Langa
Роман Донченко
Семён Марьясин

View File

@ -1,20 +0,0 @@
Copyright (c) 2008-present The pip developers (see AUTHORS.txt file)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,90 +0,0 @@
Metadata-Version: 2.1
Name: pip
Version: 24.3.1
Summary: The PyPA recommended tool for installing Python packages.
Author-email: The pip developers <distutils-sig@python.org>
License: MIT
Project-URL: Homepage, https://pip.pypa.io/
Project-URL: Documentation, https://pip.pypa.io
Project-URL: Source, https://github.com/pypa/pip
Project-URL: Changelog, https://pip.pypa.io/en/stable/news/
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Topic :: Software Development :: Build Tools
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.8
Description-Content-Type: text/x-rst
License-File: LICENSE.txt
License-File: AUTHORS.txt
pip - The Python Package Installer
==================================
.. |pypi-version| image:: https://img.shields.io/pypi/v/pip.svg
:target: https://pypi.org/project/pip/
:alt: PyPI
.. |python-versions| image:: https://img.shields.io/pypi/pyversions/pip
:target: https://pypi.org/project/pip
:alt: PyPI - Python Version
.. |docs-badge| image:: https://readthedocs.org/projects/pip/badge/?version=latest
:target: https://pip.pypa.io/en/latest
:alt: Documentation
|pypi-version| |python-versions| |docs-badge|
pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes.
Please take a look at our documentation for how to install and use pip:
* `Installation`_
* `Usage`_
We release updates regularly, with a new version every 3 months. Find more details in our documentation:
* `Release notes`_
* `Release process`_
If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms:
* `Issue tracking`_
* `Discourse channel`_
* `User IRC`_
If you want to get involved head over to GitHub to get the source code, look at our development documentation and feel free to jump on the developer mailing lists and chat rooms:
* `GitHub page`_
* `Development documentation`_
* `Development IRC`_
Code of Conduct
---------------
Everyone interacting in the pip project's codebases, issue trackers, chat
rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
.. _package installer: https://packaging.python.org/guides/tool-recommendations/
.. _Python Package Index: https://pypi.org
.. _Installation: https://pip.pypa.io/en/stable/installation/
.. _Usage: https://pip.pypa.io/en/stable/
.. _Release notes: https://pip.pypa.io/en/stable/news.html
.. _Release process: https://pip.pypa.io/en/latest/development/release-process/
.. _GitHub page: https://github.com/pypa/pip
.. _Development documentation: https://pip.pypa.io/en/latest/development
.. _Issue tracking: https://github.com/pypa/pip/issues
.. _Discourse channel: https://discuss.python.org/c/packaging
.. _User IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa
.. _Development IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa-dev
.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md

View File

@ -1,853 +0,0 @@
../../Scripts/pip.exe,sha256=yAvBcsW_HIuubmDWZJy0oARyd_kEN7UBNAhIA8k2FnM,108431
../../Scripts/pip3.13.exe,sha256=yAvBcsW_HIuubmDWZJy0oARyd_kEN7UBNAhIA8k2FnM,108431
../../Scripts/pip3.exe,sha256=yAvBcsW_HIuubmDWZJy0oARyd_kEN7UBNAhIA8k2FnM,108431
pip-24.3.1.dist-info/AUTHORS.txt,sha256=Cbb630k8EL9FkBzX9Vpi6hpYWrLSlh08eXodL5u0eLI,10925
pip-24.3.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
pip-24.3.1.dist-info/LICENSE.txt,sha256=Y0MApmnUmurmWxLGxIySTFGkzfPR_whtw0VtyLyqIQQ,1093
pip-24.3.1.dist-info/METADATA,sha256=V8iCNK1GYbC82PWsLMsASDh9AO4veocRlM4Pn9q2KFI,3677
pip-24.3.1.dist-info/RECORD,,
pip-24.3.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip-24.3.1.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
pip-24.3.1.dist-info/entry_points.txt,sha256=eeIjuzfnfR2PrhbjnbzFU6MnSS70kZLxwaHHq6M-bD0,87
pip-24.3.1.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
pip/__init__.py,sha256=faXY_neeYrA_88plEhkyhwAaYeds7wu5U1iGwP24J0s,357
pip/__main__.py,sha256=WzbhHXTbSE6gBY19mNN9m4s5o_365LOvTYSgqgbdBhE,854
pip/__pip-runner__.py,sha256=cPPWuJ6NK_k-GzfvlejLFgwzmYUROmpAR6QC3Q-vkXQ,1450
pip/__pycache__/__init__.cpython-313.pyc,,
pip/__pycache__/__main__.cpython-313.pyc,,
pip/__pycache__/__pip-runner__.cpython-313.pyc,,
pip/_internal/__init__.py,sha256=MfcoOluDZ8QMCFYal04IqOJ9q6m2V7a0aOsnI-WOxUo,513
pip/_internal/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/__pycache__/build_env.cpython-313.pyc,,
pip/_internal/__pycache__/cache.cpython-313.pyc,,
pip/_internal/__pycache__/configuration.cpython-313.pyc,,
pip/_internal/__pycache__/exceptions.cpython-313.pyc,,
pip/_internal/__pycache__/main.cpython-313.pyc,,
pip/_internal/__pycache__/pyproject.cpython-313.pyc,,
pip/_internal/__pycache__/self_outdated_check.cpython-313.pyc,,
pip/_internal/__pycache__/wheel_builder.cpython-313.pyc,,
pip/_internal/build_env.py,sha256=wsTPOWyPTKvUREUcO585OU01kbQufpdigY8fVHv3WIw,10584
pip/_internal/cache.py,sha256=Jb698p5PNigRtpW5o26wQNkkUv4MnQ94mc471wL63A0,10369
pip/_internal/cli/__init__.py,sha256=FkHBgpxxb-_gd6r1FjnNhfMOzAUYyXoXKJ6abijfcFU,132
pip/_internal/cli/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/cli/__pycache__/autocompletion.cpython-313.pyc,,
pip/_internal/cli/__pycache__/base_command.cpython-313.pyc,,
pip/_internal/cli/__pycache__/cmdoptions.cpython-313.pyc,,
pip/_internal/cli/__pycache__/command_context.cpython-313.pyc,,
pip/_internal/cli/__pycache__/index_command.cpython-313.pyc,,
pip/_internal/cli/__pycache__/main.cpython-313.pyc,,
pip/_internal/cli/__pycache__/main_parser.cpython-313.pyc,,
pip/_internal/cli/__pycache__/parser.cpython-313.pyc,,
pip/_internal/cli/__pycache__/progress_bars.cpython-313.pyc,,
pip/_internal/cli/__pycache__/req_command.cpython-313.pyc,,
pip/_internal/cli/__pycache__/spinners.cpython-313.pyc,,
pip/_internal/cli/__pycache__/status_codes.cpython-313.pyc,,
pip/_internal/cli/autocompletion.py,sha256=Lli3Mr6aDNu7ZkJJFFvwD2-hFxNI6Avz8OwMyS5TVrs,6865
pip/_internal/cli/base_command.py,sha256=F8nUcSM-Y-MQljJUe724-yxmc5viFXHyM_zH70NmIh4,8289
pip/_internal/cli/cmdoptions.py,sha256=mDqBr0d0hoztbRJs-PWtcKpqNAc7khU6ZpoesZKocT8,30110
pip/_internal/cli/command_context.py,sha256=RHgIPwtObh5KhMrd3YZTkl8zbVG-6Okml7YbFX4Ehg0,774
pip/_internal/cli/index_command.py,sha256=-0oPTruZGkLSMrWDleZ6UtcKP3G-SImRRuhH0RfVE3o,5631
pip/_internal/cli/main.py,sha256=BDZef-bWe9g9Jpr4OVs4dDf-845HJsKw835T7AqEnAc,2817
pip/_internal/cli/main_parser.py,sha256=laDpsuBDl6kyfywp9eMMA9s84jfH2TJJn-vmL0GG90w,4338
pip/_internal/cli/parser.py,sha256=VCMtduzECUV87KaHNu-xJ-wLNL82yT3x16V4XBxOAqI,10825
pip/_internal/cli/progress_bars.py,sha256=VgydyqjZvfhqpuNcFDn00QNuA9GxRe9CKrRG8jhPuKU,2723
pip/_internal/cli/req_command.py,sha256=DqeFhmUMs6o6Ev8qawAcOoYNdAZsfyKS0MZI5jsJYwQ,12250
pip/_internal/cli/spinners.py,sha256=hIJ83GerdFgFCdobIA23Jggetegl_uC4Sp586nzFbPE,5118
pip/_internal/cli/status_codes.py,sha256=sEFHUaUJbqv8iArL3HAtcztWZmGOFX01hTesSytDEh0,116
pip/_internal/commands/__init__.py,sha256=5oRO9O3dM2vGuh0bFw4HOVletryrz5HHMmmPWwJrH9U,3882
pip/_internal/commands/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/commands/__pycache__/cache.cpython-313.pyc,,
pip/_internal/commands/__pycache__/check.cpython-313.pyc,,
pip/_internal/commands/__pycache__/completion.cpython-313.pyc,,
pip/_internal/commands/__pycache__/configuration.cpython-313.pyc,,
pip/_internal/commands/__pycache__/debug.cpython-313.pyc,,
pip/_internal/commands/__pycache__/download.cpython-313.pyc,,
pip/_internal/commands/__pycache__/freeze.cpython-313.pyc,,
pip/_internal/commands/__pycache__/hash.cpython-313.pyc,,
pip/_internal/commands/__pycache__/help.cpython-313.pyc,,
pip/_internal/commands/__pycache__/index.cpython-313.pyc,,
pip/_internal/commands/__pycache__/inspect.cpython-313.pyc,,
pip/_internal/commands/__pycache__/install.cpython-313.pyc,,
pip/_internal/commands/__pycache__/list.cpython-313.pyc,,
pip/_internal/commands/__pycache__/search.cpython-313.pyc,,
pip/_internal/commands/__pycache__/show.cpython-313.pyc,,
pip/_internal/commands/__pycache__/uninstall.cpython-313.pyc,,
pip/_internal/commands/__pycache__/wheel.cpython-313.pyc,,
pip/_internal/commands/cache.py,sha256=xg76_ZFEBC6zoQ3gXLRfMZJft4z2a0RwH4GEFZC6nnU,7944
pip/_internal/commands/check.py,sha256=Hr_4eiMd9cgVDgEvjtIdw915NmL7ROIWW8enkr8slPQ,2268
pip/_internal/commands/completion.py,sha256=HT4lD0bgsflHq2IDgYfiEdp7IGGtE7s6MgI3xn0VQEw,4287
pip/_internal/commands/configuration.py,sha256=n98enwp6y0b5G6fiRQjaZo43FlJKYve_daMhN-4BRNc,9766
pip/_internal/commands/debug.py,sha256=DNDRgE9YsKrbYzU0s3VKi8rHtKF4X13CJ_br_8PUXO0,6797
pip/_internal/commands/download.py,sha256=0qB0nys6ZEPsog451lDsjL5Bx7Z97t-B80oFZKhpzKM,5273
pip/_internal/commands/freeze.py,sha256=2Vt72BYTSm9rzue6d8dNzt8idxWK4Db6Hd-anq7GQ80,3203
pip/_internal/commands/hash.py,sha256=EVVOuvGtoPEdFi8SNnmdqlCQrhCxV-kJsdwtdcCnXGQ,1703
pip/_internal/commands/help.py,sha256=gcc6QDkcgHMOuAn5UxaZwAStsRBrnGSn_yxjS57JIoM,1132
pip/_internal/commands/index.py,sha256=RAXxmJwFhVb5S1BYzb5ifX3sn9Na8v2CCVYwSMP8pao,4731
pip/_internal/commands/inspect.py,sha256=PGrY9TRTRCM3y5Ml8Bdk8DEOXquWRfscr4DRo1LOTPc,3189
pip/_internal/commands/install.py,sha256=iqesiLIZc6Op9uihMQFYRhAA2DQRZUxbM4z1BwXoFls,29428
pip/_internal/commands/list.py,sha256=oiIzSjLP6__d7dIS3q0Xb5ywsaOThBWRqMyjjKzkPdM,12769
pip/_internal/commands/search.py,sha256=fWkUQVx_gm8ebbFAlCgqtxKXT9rNahpJ-BI__3HNZpg,5626
pip/_internal/commands/show.py,sha256=IG9L5uo8w6UA4tI_IlmaxLCoNKPa5JNJCljj3NWs0OE,7507
pip/_internal/commands/uninstall.py,sha256=7pOR7enK76gimyxQbzxcG1OsyLXL3DvX939xmM8Fvtg,3892
pip/_internal/commands/wheel.py,sha256=eJRhr_qoNNxWAkkdJCNiQM7CXd4E1_YyQhsqJnBPGGg,6414
pip/_internal/configuration.py,sha256=XkAiBS0hpzsM-LF0Qu5hvPWO_Bs67-oQKRYFBuMbESs,14006
pip/_internal/distributions/__init__.py,sha256=Hq6kt6gXBgjNit5hTTWLAzeCNOKoB-N0pGYSqehrli8,858
pip/_internal/distributions/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/distributions/__pycache__/base.cpython-313.pyc,,
pip/_internal/distributions/__pycache__/installed.cpython-313.pyc,,
pip/_internal/distributions/__pycache__/sdist.cpython-313.pyc,,
pip/_internal/distributions/__pycache__/wheel.cpython-313.pyc,,
pip/_internal/distributions/base.py,sha256=QeB9qvKXDIjLdPBDE5fMgpfGqMMCr-govnuoQnGuiF8,1783
pip/_internal/distributions/installed.py,sha256=QinHFbWAQ8oE0pbD8MFZWkwlnfU1QYTccA1vnhrlYOU,842
pip/_internal/distributions/sdist.py,sha256=PlcP4a6-R6c98XnOM-b6Lkb3rsvh9iG4ok8shaanrzs,6751
pip/_internal/distributions/wheel.py,sha256=THBYfnv7VVt8mYhMYUtH13S1E7FDwtDyDfmUcl8ai0E,1317
pip/_internal/exceptions.py,sha256=2_byISIv3kSnI_9T-Esfxrt0LnTRgcUHyxu0twsHjQY,26481
pip/_internal/index/__init__.py,sha256=vpt-JeTZefh8a-FC22ZeBSXFVbuBcXSGiILhQZJaNpQ,30
pip/_internal/index/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/index/__pycache__/collector.cpython-313.pyc,,
pip/_internal/index/__pycache__/package_finder.cpython-313.pyc,,
pip/_internal/index/__pycache__/sources.cpython-313.pyc,,
pip/_internal/index/collector.py,sha256=RdPO0JLAlmyBWPAWYHPyRoGjz3GNAeTngCNkbGey_mE,16265
pip/_internal/index/package_finder.py,sha256=yRC4xsyudwKnNoU6IXvNoyqYo5ScT7lB6Wa-z2eh7cs,37666
pip/_internal/index/sources.py,sha256=lPBLK5Xiy8Q6IQMio26Wl7ocfZOKkgGklIBNyUJ23fI,8632
pip/_internal/locations/__init__.py,sha256=UaAxeZ_f93FyouuFf4p7SXYF-4WstXuEvd3LbmPCAno,14925
pip/_internal/locations/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/locations/__pycache__/_distutils.cpython-313.pyc,,
pip/_internal/locations/__pycache__/_sysconfig.cpython-313.pyc,,
pip/_internal/locations/__pycache__/base.cpython-313.pyc,,
pip/_internal/locations/_distutils.py,sha256=x6nyVLj7X11Y4khIdf-mFlxMl2FWadtVEgeb8upc_WI,6013
pip/_internal/locations/_sysconfig.py,sha256=IGzds60qsFneRogC-oeBaY7bEh3lPt_v47kMJChQXsU,7724
pip/_internal/locations/base.py,sha256=RQiPi1d4FVM2Bxk04dQhXZ2PqkeljEL2fZZ9SYqIQ78,2556
pip/_internal/main.py,sha256=r-UnUe8HLo5XFJz8inTcOOTiu_sxNhgHb6VwlGUllOI,340
pip/_internal/metadata/__init__.py,sha256=9pU3W3s-6HtjFuYhWcLTYVmSaziklPv7k2x8p7X1GmA,4339
pip/_internal/metadata/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/metadata/__pycache__/_json.cpython-313.pyc,,
pip/_internal/metadata/__pycache__/base.cpython-313.pyc,,
pip/_internal/metadata/__pycache__/pkg_resources.cpython-313.pyc,,
pip/_internal/metadata/_json.py,sha256=P0cAJrH_mtmMZvlZ16ZXm_-izA4lpr5wy08laICuiaA,2644
pip/_internal/metadata/base.py,sha256=ft0K5XNgI4ETqZnRv2-CtvgYiMOMAeGMAzxT-f6VLJA,25298
pip/_internal/metadata/importlib/__init__.py,sha256=jUUidoxnHcfITHHaAWG1G2i5fdBYklv_uJcjo2x7VYE,135
pip/_internal/metadata/importlib/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/metadata/importlib/__pycache__/_compat.cpython-313.pyc,,
pip/_internal/metadata/importlib/__pycache__/_dists.cpython-313.pyc,,
pip/_internal/metadata/importlib/__pycache__/_envs.cpython-313.pyc,,
pip/_internal/metadata/importlib/_compat.py,sha256=c6av8sP8BBjAZuFSJow1iWfygUXNM3xRTCn5nqw6B9M,2796
pip/_internal/metadata/importlib/_dists.py,sha256=anh0mLI-FYRPUhAdipd0Va3YJJc6HelCKQ0bFhY10a0,8017
pip/_internal/metadata/importlib/_envs.py,sha256=UUB980XSrDWrMpQ1_G45i0r8Hqlg_tg3IPQ63mEqbNc,7431
pip/_internal/metadata/pkg_resources.py,sha256=U07ETAINSGeSRBfWUG93E4tZZbaW_f7PGzEqZN0hulc,10542
pip/_internal/models/__init__.py,sha256=3DHUd_qxpPozfzouoqa9g9ts1Czr5qaHfFxbnxriepM,63
pip/_internal/models/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/models/__pycache__/candidate.cpython-313.pyc,,
pip/_internal/models/__pycache__/direct_url.cpython-313.pyc,,
pip/_internal/models/__pycache__/format_control.cpython-313.pyc,,
pip/_internal/models/__pycache__/index.cpython-313.pyc,,
pip/_internal/models/__pycache__/installation_report.cpython-313.pyc,,
pip/_internal/models/__pycache__/link.cpython-313.pyc,,
pip/_internal/models/__pycache__/scheme.cpython-313.pyc,,
pip/_internal/models/__pycache__/search_scope.cpython-313.pyc,,
pip/_internal/models/__pycache__/selection_prefs.cpython-313.pyc,,
pip/_internal/models/__pycache__/target_python.cpython-313.pyc,,
pip/_internal/models/__pycache__/wheel.cpython-313.pyc,,
pip/_internal/models/candidate.py,sha256=zzgFRuw_kWPjKpGw7LC0ZUMD2CQ2EberUIYs8izjdCA,753
pip/_internal/models/direct_url.py,sha256=uBtY2HHd3TO9cKQJWh0ThvE5FRr-MWRYChRU4IG9HZE,6578
pip/_internal/models/format_control.py,sha256=wtsQqSK9HaUiNxQEuB-C62eVimw6G4_VQFxV9-_KDBE,2486
pip/_internal/models/index.py,sha256=tYnL8oxGi4aSNWur0mG8DAP7rC6yuha_MwJO8xw0crI,1030
pip/_internal/models/installation_report.py,sha256=zRVZoaz-2vsrezj_H3hLOhMZCK9c7TbzWgC-jOalD00,2818
pip/_internal/models/link.py,sha256=jHax9O-9zlSzEwjBCDkx0OXjKXwBDwOuPwn-PsR8dCs,21034
pip/_internal/models/scheme.py,sha256=PakmHJM3e8OOWSZFtfz1Az7f1meONJnkGuQxFlt3wBE,575
pip/_internal/models/search_scope.py,sha256=67NEnsYY84784S-MM7ekQuo9KXLH-7MzFntXjapvAo0,4531
pip/_internal/models/selection_prefs.py,sha256=qaFfDs3ciqoXPg6xx45N1jPLqccLJw4N0s4P0PyHTQ8,2015
pip/_internal/models/target_python.py,sha256=2XaH2rZ5ZF-K5wcJbEMGEl7SqrTToDDNkrtQ2v_v_-Q,4271
pip/_internal/models/wheel.py,sha256=G7dND_s4ebPkEL7RJ1qCY0QhUUWIIK6AnjWgRATF5no,4539
pip/_internal/network/__init__.py,sha256=jf6Tt5nV_7zkARBrKojIXItgejvoegVJVKUbhAa5Ioc,50
pip/_internal/network/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/network/__pycache__/auth.cpython-313.pyc,,
pip/_internal/network/__pycache__/cache.cpython-313.pyc,,
pip/_internal/network/__pycache__/download.cpython-313.pyc,,
pip/_internal/network/__pycache__/lazy_wheel.cpython-313.pyc,,
pip/_internal/network/__pycache__/session.cpython-313.pyc,,
pip/_internal/network/__pycache__/utils.cpython-313.pyc,,
pip/_internal/network/__pycache__/xmlrpc.cpython-313.pyc,,
pip/_internal/network/auth.py,sha256=D4gASjUrqoDFlSt6gQ767KAAjv6PUyJU0puDlhXNVRE,20809
pip/_internal/network/cache.py,sha256=48A971qCzKNFvkb57uGEk7-0xaqPS0HWj2711QNTxkU,3935
pip/_internal/network/download.py,sha256=FLOP29dPYECBiAi7eEjvAbNkyzaKNqbyjOT2m8HPW8U,6048
pip/_internal/network/lazy_wheel.py,sha256=PBdoMoNQQIA84Fhgne38jWF52W4x_KtsHjxgv4dkRKA,7622
pip/_internal/network/session.py,sha256=XmanBKjVwPFmh1iJ58q6TDh9xabH37gREuQJ_feuZGA,18741
pip/_internal/network/utils.py,sha256=Inaxel-NxBu4PQWkjyErdnfewsFCcgHph7dzR1-FboY,4088
pip/_internal/network/xmlrpc.py,sha256=sAxzOacJ-N1NXGPvap9jC3zuYWSnnv3GXtgR2-E2APA,1838
pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_internal/operations/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/operations/__pycache__/check.cpython-313.pyc,,
pip/_internal/operations/__pycache__/freeze.cpython-313.pyc,,
pip/_internal/operations/__pycache__/prepare.cpython-313.pyc,,
pip/_internal/operations/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_internal/operations/build/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/operations/build/__pycache__/build_tracker.cpython-313.pyc,,
pip/_internal/operations/build/__pycache__/metadata.cpython-313.pyc,,
pip/_internal/operations/build/__pycache__/metadata_editable.cpython-313.pyc,,
pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-313.pyc,,
pip/_internal/operations/build/__pycache__/wheel.cpython-313.pyc,,
pip/_internal/operations/build/__pycache__/wheel_editable.cpython-313.pyc,,
pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-313.pyc,,
pip/_internal/operations/build/build_tracker.py,sha256=-ARW_TcjHCOX7D2NUOGntB4Fgc6b4aolsXkAK6BWL7w,4774
pip/_internal/operations/build/metadata.py,sha256=9S0CUD8U3QqZeXp-Zyt8HxwU90lE4QrnYDgrqZDzBnc,1422
pip/_internal/operations/build/metadata_editable.py,sha256=VLL7LvntKE8qxdhUdEJhcotFzUsOSI8NNS043xULKew,1474
pip/_internal/operations/build/metadata_legacy.py,sha256=8i6i1QZX9m_lKPStEFsHKM0MT4a-CD408JOw99daLmo,2190
pip/_internal/operations/build/wheel.py,sha256=sT12FBLAxDC6wyrDorh8kvcZ1jG5qInCRWzzP-UkJiQ,1075
pip/_internal/operations/build/wheel_editable.py,sha256=yOtoH6zpAkoKYEUtr8FhzrYnkNHQaQBjWQ2HYae1MQg,1417
pip/_internal/operations/build/wheel_legacy.py,sha256=K-6kNhmj-1xDF45ny1yheMerF0ui4EoQCLzEoHh6-tc,3045
pip/_internal/operations/check.py,sha256=L24vRL8VWbyywdoeAhM89WCd8zLTnjIbULlKelUgIec,5912
pip/_internal/operations/freeze.py,sha256=V59yEyCSz_YhZuhH09-6aV_zvYBMrS_IxFFNqn2QzlA,9864
pip/_internal/operations/install/__init__.py,sha256=mX7hyD2GNBO2mFGokDQ30r_GXv7Y_PLdtxcUv144e-s,51
pip/_internal/operations/install/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/operations/install/__pycache__/editable_legacy.cpython-313.pyc,,
pip/_internal/operations/install/__pycache__/wheel.cpython-313.pyc,,
pip/_internal/operations/install/editable_legacy.py,sha256=PoEsNEPGbIZ2yQphPsmYTKLOCMs4gv5OcCdzW124NcA,1283
pip/_internal/operations/install/wheel.py,sha256=X5Iz9yUg5LlK5VNQ9g2ikc6dcRu8EPi_SUi5iuEDRgo,27615
pip/_internal/operations/prepare.py,sha256=joWJwPkuqGscQgVNImLK71e9hRapwKvRCM8HclysmvU,28118
pip/_internal/pyproject.py,sha256=rw4fwlptDp1hZgYoplwbAGwWA32sWQkp7ysf8Ju6iXc,7287
pip/_internal/req/__init__.py,sha256=HxBFtZy_BbCclLgr26waMtpzYdO5T3vxePvpGAXSt5s,2653
pip/_internal/req/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/req/__pycache__/constructors.cpython-313.pyc,,
pip/_internal/req/__pycache__/req_file.cpython-313.pyc,,
pip/_internal/req/__pycache__/req_install.cpython-313.pyc,,
pip/_internal/req/__pycache__/req_set.cpython-313.pyc,,
pip/_internal/req/__pycache__/req_uninstall.cpython-313.pyc,,
pip/_internal/req/constructors.py,sha256=v1qzCN1mIldwx-nCrPc8JO4lxkm3Fv8M5RWvt8LISjc,18430
pip/_internal/req/req_file.py,sha256=gOOJTzL-mDRPcQhjwqjDrjn4V-3rK9TnEFnU3v8RA4Q,18752
pip/_internal/req/req_install.py,sha256=yhT98NGDoAEk03jznTJnYCznzhiMEEA2ocgsUG_dcNU,35788
pip/_internal/req/req_set.py,sha256=j3esG0s6SzoVReX9rWn4rpYNtyET_fwxbwJPRimvRxo,2858
pip/_internal/req/req_uninstall.py,sha256=qzDIxJo-OETWqGais7tSMCDcWbATYABT-Tid3ityF0s,23853
pip/_internal/resolution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_internal/resolution/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/resolution/__pycache__/base.cpython-313.pyc,,
pip/_internal/resolution/base.py,sha256=qlmh325SBVfvG6Me9gc5Nsh5sdwHBwzHBq6aEXtKsLA,583
pip/_internal/resolution/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_internal/resolution/legacy/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/resolution/legacy/__pycache__/resolver.cpython-313.pyc,,
pip/_internal/resolution/legacy/resolver.py,sha256=3HZiJBRd1FTN6jQpI4qRO8-TbLYeIbUTS6PFvXnXs2w,24068
pip/_internal/resolution/resolvelib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/resolution/resolvelib/__pycache__/base.cpython-313.pyc,,
pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-313.pyc,,
pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-313.pyc,,
pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-313.pyc,,
pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-313.pyc,,
pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-313.pyc,,
pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-313.pyc,,
pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-313.pyc,,
pip/_internal/resolution/resolvelib/base.py,sha256=DCf669FsqyQY5uqXeePDHQY1e4QO-pBzWH8O0s9-K94,5023
pip/_internal/resolution/resolvelib/candidates.py,sha256=5UZ1upNnmqsP-nmEZaDYxaBgCoejw_e2WVGmmAvBxXc,20001
pip/_internal/resolution/resolvelib/factory.py,sha256=511CaUR41LqjALuFafLVfx15WRvMhxYTdjQCoSvp4gw,32661
pip/_internal/resolution/resolvelib/found_candidates.py,sha256=9hrTyQqFvl9I7Tji79F1AxHv39Qh1rkJ_7deSHSMfQc,6383
pip/_internal/resolution/resolvelib/provider.py,sha256=bcsFnYvlmtB80cwVdW1fIwgol8ZNr1f1VHyRTkz47SM,9935
pip/_internal/resolution/resolvelib/reporter.py,sha256=00JtoXEkTlw0-rl_sl54d71avwOsJHt9GGHcrj5Sza0,3168
pip/_internal/resolution/resolvelib/requirements.py,sha256=7JG4Z72e5Yk4vU0S5ulGvbqTy4FMQGYhY5zQhX9zTtY,8065
pip/_internal/resolution/resolvelib/resolver.py,sha256=nLJOsVMEVi2gQUVJoUFKMZAeu2f7GRMjGMvNSWyz0Bc,12592
pip/_internal/self_outdated_check.py,sha256=pkjQixuWyQ1vrVxZAaYD6SSHgXuFUnHZybXEWTkh0S0,8145
pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_internal/utils/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/utils/__pycache__/_jaraco_text.cpython-313.pyc,,
pip/_internal/utils/__pycache__/_log.cpython-313.pyc,,
pip/_internal/utils/__pycache__/appdirs.cpython-313.pyc,,
pip/_internal/utils/__pycache__/compat.cpython-313.pyc,,
pip/_internal/utils/__pycache__/compatibility_tags.cpython-313.pyc,,
pip/_internal/utils/__pycache__/datetime.cpython-313.pyc,,
pip/_internal/utils/__pycache__/deprecation.cpython-313.pyc,,
pip/_internal/utils/__pycache__/direct_url_helpers.cpython-313.pyc,,
pip/_internal/utils/__pycache__/egg_link.cpython-313.pyc,,
pip/_internal/utils/__pycache__/encoding.cpython-313.pyc,,
pip/_internal/utils/__pycache__/entrypoints.cpython-313.pyc,,
pip/_internal/utils/__pycache__/filesystem.cpython-313.pyc,,
pip/_internal/utils/__pycache__/filetypes.cpython-313.pyc,,
pip/_internal/utils/__pycache__/glibc.cpython-313.pyc,,
pip/_internal/utils/__pycache__/hashes.cpython-313.pyc,,
pip/_internal/utils/__pycache__/logging.cpython-313.pyc,,
pip/_internal/utils/__pycache__/misc.cpython-313.pyc,,
pip/_internal/utils/__pycache__/packaging.cpython-313.pyc,,
pip/_internal/utils/__pycache__/retry.cpython-313.pyc,,
pip/_internal/utils/__pycache__/setuptools_build.cpython-313.pyc,,
pip/_internal/utils/__pycache__/subprocess.cpython-313.pyc,,
pip/_internal/utils/__pycache__/temp_dir.cpython-313.pyc,,
pip/_internal/utils/__pycache__/unpacking.cpython-313.pyc,,
pip/_internal/utils/__pycache__/urls.cpython-313.pyc,,
pip/_internal/utils/__pycache__/virtualenv.cpython-313.pyc,,
pip/_internal/utils/__pycache__/wheel.cpython-313.pyc,,
pip/_internal/utils/_jaraco_text.py,sha256=M15uUPIh5NpP1tdUGBxRau6q1ZAEtI8-XyLEETscFfE,3350
pip/_internal/utils/_log.py,sha256=-jHLOE_THaZz5BFcCnoSL9EYAtJ0nXem49s9of4jvKw,1015
pip/_internal/utils/appdirs.py,sha256=swgcTKOm3daLeXTW6v5BUS2Ti2RvEnGRQYH_yDXklAo,1665
pip/_internal/utils/compat.py,sha256=ckkFveBiYQjRWjkNsajt_oWPS57tJvE8XxoC4OIYgCY,2399
pip/_internal/utils/compatibility_tags.py,sha256=OWq5axHpW-MEEPztGdvgADrgJPAcV9a88Rxm4Z8VBs8,6272
pip/_internal/utils/datetime.py,sha256=m21Y3wAtQc-ji6Veb6k_M5g6A0ZyFI4egchTdnwh-pQ,242
pip/_internal/utils/deprecation.py,sha256=k7Qg_UBAaaTdyq82YVARA6D7RmcGTXGv7fnfcgigj4Q,3707
pip/_internal/utils/direct_url_helpers.py,sha256=r2MRtkVDACv9AGqYODBUC9CjwgtsUU1s68hmgfCJMtA,3196
pip/_internal/utils/egg_link.py,sha256=0FePZoUYKv4RGQ2t6x7w5Z427wbA_Uo3WZnAkrgsuqo,2463
pip/_internal/utils/encoding.py,sha256=qqsXDtiwMIjXMEiIVSaOjwH5YmirCaK-dIzb6-XJsL0,1169
pip/_internal/utils/entrypoints.py,sha256=YlhLTRl2oHBAuqhc-zmL7USS67TPWVHImjeAQHreZTQ,3064
pip/_internal/utils/filesystem.py,sha256=ajvA-q4ocliW9kPp8Yquh-4vssXbu-UKbo5FV9V4X64,4950
pip/_internal/utils/filetypes.py,sha256=i8XAQ0eFCog26Fw9yV0Yb1ygAqKYB1w9Cz9n0fj8gZU,716
pip/_internal/utils/glibc.py,sha256=vUkWq_1pJuzcYNcGKLlQmABoUiisK8noYY1yc8Wq4w4,3734
pip/_internal/utils/hashes.py,sha256=XGGLL0AG8-RhWnyz87xF6MFZ--BKadHU35D47eApCKI,4972
pip/_internal/utils/logging.py,sha256=7BFKB1uFjdxD5crM-GtwA5T2qjbQ2LPD-gJDuJeDNTg,11606
pip/_internal/utils/misc.py,sha256=NRV0_2fFhzy1jhvInSBv4dqCmTwct8PV7Kp0m-BPRGM,23530
pip/_internal/utils/packaging.py,sha256=iI3LH43lVNR4hWBOqF6lFsZq4aycb2j0UcHlmDmcqUg,2109
pip/_internal/utils/retry.py,sha256=mhFbykXjhTnZfgzeuy-vl9c8nECnYn_CMtwNJX2tYzQ,1392
pip/_internal/utils/setuptools_build.py,sha256=ouXpud-jeS8xPyTPsXJ-m34NPvK5os45otAzdSV_IJE,4435
pip/_internal/utils/subprocess.py,sha256=EsvqSRiSMHF98T8Txmu6NLU3U--MpTTQjtNgKP0P--M,8988
pip/_internal/utils/temp_dir.py,sha256=5qOXe8M4JeY6vaFQM867d5zkp1bSwMZ-KT5jymmP0Zg,9310
pip/_internal/utils/unpacking.py,sha256=eyDkSsk4nW8ZfiSjNzJduCznpHyaGHVv3ak_LMGsiEM,11951
pip/_internal/utils/urls.py,sha256=qceSOZb5lbNDrHNsv7_S4L4Ytszja5NwPKUMnZHbYnM,1599
pip/_internal/utils/virtualenv.py,sha256=S6f7csYorRpiD6cvn3jISZYc3I8PJC43H5iMFpRAEDU,3456
pip/_internal/utils/wheel.py,sha256=b442jkydFHjXzDy6cMR7MpzWBJ1Q82hR5F33cmcHV3g,4494
pip/_internal/vcs/__init__.py,sha256=UAqvzpbi0VbZo3Ub6skEeZAw-ooIZR-zX_WpCbxyCoU,596
pip/_internal/vcs/__pycache__/__init__.cpython-313.pyc,,
pip/_internal/vcs/__pycache__/bazaar.cpython-313.pyc,,
pip/_internal/vcs/__pycache__/git.cpython-313.pyc,,
pip/_internal/vcs/__pycache__/mercurial.cpython-313.pyc,,
pip/_internal/vcs/__pycache__/subversion.cpython-313.pyc,,
pip/_internal/vcs/__pycache__/versioncontrol.cpython-313.pyc,,
pip/_internal/vcs/bazaar.py,sha256=EKStcQaKpNu0NK4p5Q10Oc4xb3DUxFw024XrJy40bFQ,3528
pip/_internal/vcs/git.py,sha256=3tpc9LQA_J4IVW5r5NvWaaSeDzcmJOrSFZN0J8vIKfU,18177
pip/_internal/vcs/mercurial.py,sha256=oULOhzJ2Uie-06d1omkL-_Gc6meGaUkyogvqG9ZCyPs,5249
pip/_internal/vcs/subversion.py,sha256=ddTugHBqHzV3ebKlU5QXHPN4gUqlyXbOx8q8NgXKvs8,11735
pip/_internal/vcs/versioncontrol.py,sha256=cvf_-hnTAjQLXJ3d17FMNhQfcO1AcKWUF10tfrYyP-c,22440
pip/_internal/wheel_builder.py,sha256=DL3A8LKeRj_ACp11WS5wSgASgPFqeyAeXJKdXfmaWXU,11799
pip/_vendor/__init__.py,sha256=JYuAXvClhInxIrA2FTp5p-uuWVL7WV6-vEpTs46-Qh4,4873
pip/_vendor/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/__pycache__/typing_extensions.cpython-313.pyc,,
pip/_vendor/cachecontrol/__init__.py,sha256=GiYoagwPEiJ_xR_lbwWGaoCiPtF_rz4isjfjdDAgHU4,676
pip/_vendor/cachecontrol/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-313.pyc,,
pip/_vendor/cachecontrol/__pycache__/adapter.cpython-313.pyc,,
pip/_vendor/cachecontrol/__pycache__/cache.cpython-313.pyc,,
pip/_vendor/cachecontrol/__pycache__/controller.cpython-313.pyc,,
pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-313.pyc,,
pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-313.pyc,,
pip/_vendor/cachecontrol/__pycache__/serialize.cpython-313.pyc,,
pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-313.pyc,,
pip/_vendor/cachecontrol/_cmd.py,sha256=iist2EpzJvDVIhMAxXq8iFnTBsiZAd6iplxfmNboNyk,1737
pip/_vendor/cachecontrol/adapter.py,sha256=fByO_Pd_EOemjWbuocvBWdN85xT0q_TBm2lxS6vD4fk,6355
pip/_vendor/cachecontrol/cache.py,sha256=OTQj72tUf8C1uEgczdl3Gc8vkldSzsTITKtDGKMx4z8,1952
pip/_vendor/cachecontrol/caches/__init__.py,sha256=dtrrroK5BnADR1GWjCZ19aZ0tFsMfvFBtLQQU1sp_ag,303
pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-313.pyc,,
pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-313.pyc,,
pip/_vendor/cachecontrol/caches/file_cache.py,sha256=9AlmmTJc6cslb6k5z_6q0sGPHVrMj8zv-uWy-simmfE,5406
pip/_vendor/cachecontrol/caches/redis_cache.py,sha256=9rmqwtYu_ljVkW6_oLqbC7EaX_a8YT_yLuna-eS0dgo,1386
pip/_vendor/cachecontrol/controller.py,sha256=o-ejGJlBmpKK8QQLyTPJj0t7siU8XVHXuV8MCybCxQ8,18575
pip/_vendor/cachecontrol/filewrapper.py,sha256=STttGmIPBvZzt2b51dUOwoWX5crcMCpKZOisM3f5BNc,4292
pip/_vendor/cachecontrol/heuristics.py,sha256=IYe4QmHERWsMvtxNrp920WeaIsaTTyqLB14DSheSbtY,4834
pip/_vendor/cachecontrol/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/cachecontrol/serialize.py,sha256=HQd2IllQ05HzPkVLMXTF2uX5mjEQjDBkxCqUJUODpZk,5163
pip/_vendor/cachecontrol/wrapper.py,sha256=hsGc7g8QGQTT-4f8tgz3AM5qwScg6FO0BSdLSRdEvpU,1417
pip/_vendor/certifi/__init__.py,sha256=p_GYZrjUwPBUhpLlCZoGb0miKBKSqDAyZC5DvIuqbHQ,94
pip/_vendor/certifi/__main__.py,sha256=1k3Cr95vCxxGRGDljrW3wMdpZdL3Nhf0u1n-k2qdsCY,255
pip/_vendor/certifi/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/certifi/__pycache__/__main__.cpython-313.pyc,,
pip/_vendor/certifi/__pycache__/core.cpython-313.pyc,,
pip/_vendor/certifi/cacert.pem,sha256=lO3rZukXdPyuk6BWUJFOKQliWaXH6HGh9l1GGrUgG0c,299427
pip/_vendor/certifi/core.py,sha256=2SRT5rIcQChFDbe37BQa-kULxAgJ8qN6l1jfqTp4HIs,4486
pip/_vendor/certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/distlib/__init__.py,sha256=dcwgYGYGQqAEawBXPDtIx80DO_3cOmFv8HTc8JMzknQ,625
pip/_vendor/distlib/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/compat.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/database.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/index.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/locators.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/manifest.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/markers.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/metadata.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/resources.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/scripts.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/util.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/version.cpython-313.pyc,,
pip/_vendor/distlib/__pycache__/wheel.cpython-313.pyc,,
pip/_vendor/distlib/compat.py,sha256=2jRSjRI4o-vlXeTK2BCGIUhkc6e9ZGhSsacRM5oseTw,41467
pip/_vendor/distlib/database.py,sha256=mHy_LxiXIsIVRb-T0-idBrVLw3Ffij5teHCpbjmJ9YU,51160
pip/_vendor/distlib/index.py,sha256=lTbw268rRhj8dw1sib3VZ_0EhSGgoJO3FKJzSFMOaeA,20797
pip/_vendor/distlib/locators.py,sha256=oBeAZpFuPQSY09MgNnLfQGGAXXvVO96BFpZyKMuK4tM,51026
pip/_vendor/distlib/manifest.py,sha256=3qfmAmVwxRqU1o23AlfXrQGZzh6g_GGzTAP_Hb9C5zQ,14168
pip/_vendor/distlib/markers.py,sha256=X6sDvkFGcYS8gUW8hfsWuKEKAqhQZAJ7iXOMLxRYjYk,5164
pip/_vendor/distlib/metadata.py,sha256=zil3sg2EUfLXVigljY2d_03IJt-JSs7nX-73fECMX2s,38724
pip/_vendor/distlib/resources.py,sha256=LwbPksc0A1JMbi6XnuPdMBUn83X7BPuFNWqPGEKI698,10820
pip/_vendor/distlib/scripts.py,sha256=BJliaDAZaVB7WAkwokgC3HXwLD2iWiHaVI50H7C6eG8,18608
pip/_vendor/distlib/t32.exe,sha256=a0GV5kCoWsMutvliiCKmIgV98eRZ33wXoS-XrqvJQVs,97792
pip/_vendor/distlib/t64-arm.exe,sha256=68TAa32V504xVBnufojh0PcenpR3U4wAqTqf-MZqbPw,182784
pip/_vendor/distlib/t64.exe,sha256=gaYY8hy4fbkHYTTnA4i26ct8IQZzkBG2pRdy0iyuBrc,108032
pip/_vendor/distlib/util.py,sha256=vMPGvsS4j9hF6Y9k3Tyom1aaHLb0rFmZAEyzeAdel9w,66682
pip/_vendor/distlib/version.py,sha256=s5VIs8wBn0fxzGxWM_aA2ZZyx525HcZbMvcTlTyZ3Rg,23727
pip/_vendor/distlib/w32.exe,sha256=R4csx3-OGM9kL4aPIzQKRo5TfmRSHZo6QWyLhDhNBks,91648
pip/_vendor/distlib/w64-arm.exe,sha256=xdyYhKj0WDcVUOCb05blQYvzdYIKMbmJn2SZvzkcey4,168448
pip/_vendor/distlib/w64.exe,sha256=ejGf-rojoBfXseGLpya6bFTFPWRG21X5KvU8J5iU-K0,101888
pip/_vendor/distlib/wheel.py,sha256=DFIVguEQHCdxnSdAO0dfFsgMcvVZitg7bCOuLwZ7A_s,43979
pip/_vendor/distro/__init__.py,sha256=2fHjF-SfgPvjyNZ1iHh_wjqWdR_Yo5ODHwZC0jLBPhc,981
pip/_vendor/distro/__main__.py,sha256=bu9d3TifoKciZFcqRBuygV3GSuThnVD_m2IK4cz96Vs,64
pip/_vendor/distro/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/distro/__pycache__/__main__.cpython-313.pyc,,
pip/_vendor/distro/__pycache__/distro.cpython-313.pyc,,
pip/_vendor/distro/distro.py,sha256=XqbefacAhDT4zr_trnbA15eY8vdK4GTghgmvUGrEM_4,49430
pip/_vendor/distro/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/idna/__init__.py,sha256=KJQN1eQBr8iIK5SKrJ47lXvxG0BJ7Lm38W4zT0v_8lk,849
pip/_vendor/idna/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/idna/__pycache__/codec.cpython-313.pyc,,
pip/_vendor/idna/__pycache__/compat.cpython-313.pyc,,
pip/_vendor/idna/__pycache__/core.cpython-313.pyc,,
pip/_vendor/idna/__pycache__/idnadata.cpython-313.pyc,,
pip/_vendor/idna/__pycache__/intranges.cpython-313.pyc,,
pip/_vendor/idna/__pycache__/package_data.cpython-313.pyc,,
pip/_vendor/idna/__pycache__/uts46data.cpython-313.pyc,,
pip/_vendor/idna/codec.py,sha256=PS6m-XmdST7Wj7J7ulRMakPDt5EBJyYrT3CPtjh-7t4,3426
pip/_vendor/idna/compat.py,sha256=0_sOEUMT4CVw9doD3vyRhX80X19PwqFoUBs7gWsFME4,321
pip/_vendor/idna/core.py,sha256=lyhpoe2vulEaB_65xhXmoKgO-xUqFDvcwxu5hpNNO4E,12663
pip/_vendor/idna/idnadata.py,sha256=dqRwytzkjIHMBa2R1lYvHDwACenZPt8eGVu1Y8UBE-E,78320
pip/_vendor/idna/intranges.py,sha256=YBr4fRYuWH7kTKS2tXlFjM24ZF1Pdvcir-aywniInqg,1881
pip/_vendor/idna/package_data.py,sha256=Tkt0KnIeyIlnHddOaz9WSkkislNgokJAuE-p5GorMqo,21
pip/_vendor/idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/idna/uts46data.py,sha256=1KuksWqLuccPXm2uyRVkhfiFLNIhM_H2m4azCcnOqEU,206503
pip/_vendor/msgpack/__init__.py,sha256=gsMP7JTECZNUSjvOyIbdhNOkpB9Z8BcGwabVGY2UcdQ,1077
pip/_vendor/msgpack/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/msgpack/__pycache__/exceptions.cpython-313.pyc,,
pip/_vendor/msgpack/__pycache__/ext.cpython-313.pyc,,
pip/_vendor/msgpack/__pycache__/fallback.cpython-313.pyc,,
pip/_vendor/msgpack/exceptions.py,sha256=dCTWei8dpkrMsQDcjQk74ATl9HsIBH0ybt8zOPNqMYc,1081
pip/_vendor/msgpack/ext.py,sha256=fKp00BqDLjUtZnPd70Llr138zk8JsCuSpJkkZ5S4dt8,5629
pip/_vendor/msgpack/fallback.py,sha256=wdUWJkWX2gzfRW9BBCTOuIE1Wvrf5PtBtR8ZtY7G_EE,33175
pip/_vendor/packaging/__init__.py,sha256=dtw2bNmWCQ9WnMoK3bk_elL1svSlikXtLpZhCFIB9SE,496
pip/_vendor/packaging/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/_elffile.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/_manylinux.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/_musllinux.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/_parser.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/_structures.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/_tokenizer.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/markers.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/metadata.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/requirements.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/specifiers.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/tags.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/utils.cpython-313.pyc,,
pip/_vendor/packaging/__pycache__/version.cpython-313.pyc,,
pip/_vendor/packaging/_elffile.py,sha256=_LcJW4YNKywYsl4169B2ukKRqwxjxst_8H0FRVQKlz8,3282
pip/_vendor/packaging/_manylinux.py,sha256=Xo4V0PZz8sbuVCbTni0t1CR0AHeir_7ib4lTmV8scD4,9586
pip/_vendor/packaging/_musllinux.py,sha256=p9ZqNYiOItGee8KcZFeHF_YcdhVwGHdK6r-8lgixvGQ,2694
pip/_vendor/packaging/_parser.py,sha256=s_TvTvDNK0NrM2QB3VKThdWFM4Nc0P6JnkObkl3MjpM,10236
pip/_vendor/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431
pip/_vendor/packaging/_tokenizer.py,sha256=J6v5H7Jzvb-g81xp_2QACKwO7LxHQA6ikryMU7zXwN8,5273
pip/_vendor/packaging/markers.py,sha256=dWKSqn5Sp-jDmOG-W3GfLHKjwhf1IsznbT71VlBoB5M,10671
pip/_vendor/packaging/metadata.py,sha256=KINuSkJ12u-SyoKNTy_pHNGAfMUtxNvZ53qA1zAKcKI,32349
pip/_vendor/packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/packaging/requirements.py,sha256=gYyRSAdbrIyKDY66ugIDUQjRMvxkH2ALioTmX3tnL6o,2947
pip/_vendor/packaging/specifiers.py,sha256=HfGgfNJRvrzC759gnnoojHyiWs_DYmcw5PEh5jHH-YE,39738
pip/_vendor/packaging/tags.py,sha256=Fo6_cit95-7QfcMb16XtI7AUiSMgdwA_hCO_9lV2pz4,21388
pip/_vendor/packaging/utils.py,sha256=NAdYUwnlAOpkat_RthavX8a07YuVxgGL_vwrx73GSDM,5287
pip/_vendor/packaging/version.py,sha256=wE4sSVlF-d1H6HFC1vszEe35CwTig_fh4HHIFg95hFE,16210
pip/_vendor/pkg_resources/__init__.py,sha256=jrhDRbOubP74QuPXxd7U7Po42PH2l-LZ2XfcO7llpZ4,124463
pip/_vendor/pkg_resources/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/platformdirs/__init__.py,sha256=FTA6LGNm40GwNZt3gG3uLAacWvf2E_2HTmH0rAALGR8,22285
pip/_vendor/platformdirs/__main__.py,sha256=jBJ8zb7Mpx5ebcqF83xrpO94MaeCpNGHVf9cvDN2JLg,1505
pip/_vendor/platformdirs/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/platformdirs/__pycache__/__main__.cpython-313.pyc,,
pip/_vendor/platformdirs/__pycache__/android.cpython-313.pyc,,
pip/_vendor/platformdirs/__pycache__/api.cpython-313.pyc,,
pip/_vendor/platformdirs/__pycache__/macos.cpython-313.pyc,,
pip/_vendor/platformdirs/__pycache__/unix.cpython-313.pyc,,
pip/_vendor/platformdirs/__pycache__/version.cpython-313.pyc,,
pip/_vendor/platformdirs/__pycache__/windows.cpython-313.pyc,,
pip/_vendor/platformdirs/android.py,sha256=xZXY9Jd46WOsxT2U6-5HsNtDZ-IQqxcEUrBLl3hYk4o,9016
pip/_vendor/platformdirs/api.py,sha256=QBYdUac2eC521ek_y53uD1Dcq-lJX8IgSRVd4InC6uc,8996
pip/_vendor/platformdirs/macos.py,sha256=wftsbsvq6nZ0WORXSiCrZNkRHz_WKuktl0a6mC7MFkI,5580
pip/_vendor/platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/platformdirs/unix.py,sha256=Cci9Wqt35dAMsg6HT9nRGHSBW5obb0pR3AE1JJnsCXg,10643
pip/_vendor/platformdirs/version.py,sha256=r7F76tZRjgQKzrpx_I0_ZMQOMU-PS7eGnHD7zEK3KB0,411
pip/_vendor/platformdirs/windows.py,sha256=IFpiohUBwxPtCzlyKwNtxyW4Jk8haa6W8o59mfrDXVo,10125
pip/_vendor/pygments/__init__.py,sha256=7N1oiaWulw_nCsTY4EEixYLz15pWY5u4uPAFFi-ielU,2983
pip/_vendor/pygments/__main__.py,sha256=isIhBxLg65nLlXukG4VkMuPfNdd7gFzTZ_R_z3Q8diY,353
pip/_vendor/pygments/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/__main__.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/cmdline.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/console.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/filter.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/formatter.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/lexer.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/modeline.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/plugin.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/regexopt.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/scanner.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/sphinxext.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/style.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/token.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/unistring.cpython-313.pyc,,
pip/_vendor/pygments/__pycache__/util.cpython-313.pyc,,
pip/_vendor/pygments/cmdline.py,sha256=LIVzmAunlk9sRJJp54O4KRy9GDIN4Wu13v9p9QzfGPM,23656
pip/_vendor/pygments/console.py,sha256=yhP9UsLAVmWKVQf2446JJewkA7AiXeeTf4Ieg3Oi2fU,1718
pip/_vendor/pygments/filter.py,sha256=_ADNPCskD8_GmodHi6_LoVgPU3Zh336aBCT5cOeTMs0,1910
pip/_vendor/pygments/filters/__init__.py,sha256=RdedK2KWKXlKwR7cvkfr3NUj9YiZQgMgilRMFUg2jPA,40392
pip/_vendor/pygments/filters/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/pygments/formatter.py,sha256=jDWBTndlBH2Z5IYZFVDnP0qn1CaTQjTWt7iAGtCnJEg,4390
pip/_vendor/pygments/formatters/__init__.py,sha256=8No-NUs8rBTSSBJIv4hSEQt2M0cFB4hwAT0snVc2QGE,5385
pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/bbcode.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/groff.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/html.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/img.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/irc.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/latex.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/other.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/pangomarkup.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/rtf.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/svg.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/terminal.cpython-313.pyc,,
pip/_vendor/pygments/formatters/__pycache__/terminal256.cpython-313.pyc,,
pip/_vendor/pygments/formatters/_mapping.py,sha256=1Cw37FuQlNacnxRKmtlPX4nyLoX9_ttko5ZwscNUZZ4,4176
pip/_vendor/pygments/formatters/bbcode.py,sha256=3JQLI45tcrQ_kRUMjuab6C7Hb0XUsbVWqqbSn9cMjkI,3320
pip/_vendor/pygments/formatters/groff.py,sha256=M39k0PaSSZRnxWjqBSVPkF0mu1-Vr7bm6RsFvs-CNN4,5106
pip/_vendor/pygments/formatters/html.py,sha256=SE2jc3YCqbMS3rZW9EAmDlAUhdVxJ52gA4dileEvCGU,35669
pip/_vendor/pygments/formatters/img.py,sha256=MwA4xWPLOwh6j7Yc6oHzjuqSPt0M1fh5r-5BTIIUfsU,23287
pip/_vendor/pygments/formatters/irc.py,sha256=dp1Z0l_ObJ5NFh9MhqLGg5ptG5hgJqedT2Vkutt9v0M,4981
pip/_vendor/pygments/formatters/latex.py,sha256=XMmhOCqUKDBQtG5mGJNAFYxApqaC5puo5cMmPfK3944,19306
pip/_vendor/pygments/formatters/other.py,sha256=56PMJOliin-rAUdnRM0i1wsV1GdUPd_dvQq0_UPfF9c,5034
pip/_vendor/pygments/formatters/pangomarkup.py,sha256=y16U00aVYYEFpeCfGXlYBSMacG425CbfoG8oKbKegIg,2218
pip/_vendor/pygments/formatters/rtf.py,sha256=ZT90dmcKyJboIB0mArhL7IhE467GXRN0G7QAUgG03To,11957
pip/_vendor/pygments/formatters/svg.py,sha256=KKsiophPupHuxm0So-MsbQEWOT54IAiSF7hZPmxtKXE,7174
pip/_vendor/pygments/formatters/terminal.py,sha256=AojNG4MlKq2L6IsC_VnXHu4AbHCBn9Otog6u45XvxeI,4674
pip/_vendor/pygments/formatters/terminal256.py,sha256=kGkNUVo3FpwjytIDS0if79EuUoroAprcWt3igrcIqT0,11753
pip/_vendor/pygments/lexer.py,sha256=TYHDt___gNW4axTl2zvPZff-VQi8fPaIh5OKRcVSjUM,35349
pip/_vendor/pygments/lexers/__init__.py,sha256=pIlxyQJuu_syh9lE080cq8ceVbEVcKp0osAFU5fawJU,12115
pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-313.pyc,,
pip/_vendor/pygments/lexers/__pycache__/python.cpython-313.pyc,,
pip/_vendor/pygments/lexers/_mapping.py,sha256=61-h3zr103m01OS5BUq_AfUiL9YI06Ves9ipQ7k4vr4,76097
pip/_vendor/pygments/lexers/python.py,sha256=2J_YJrPTr_A6fJY_qKiKv0GpgPwHMrlMSeo59qN3fe4,53687
pip/_vendor/pygments/modeline.py,sha256=gtRYZBS-CKOCDXHhGZqApboHBaZwGH8gznN3O6nuxj4,1005
pip/_vendor/pygments/plugin.py,sha256=ioeJ3QeoJ-UQhZpY9JL7vbxsTVuwwM7BCu-Jb8nN0AU,1891
pip/_vendor/pygments/regexopt.py,sha256=Hky4EB13rIXEHQUNkwmCrYqtIlnXDehNR3MztafZ43w,3072
pip/_vendor/pygments/scanner.py,sha256=NDy3ofK_fHRFK4hIDvxpamG871aewqcsIb6sgTi7Fhk,3092
pip/_vendor/pygments/sphinxext.py,sha256=iOptJBcqOGPwMEJ2p70PvwpZPIGdvdZ8dxvq6kzxDgA,7981
pip/_vendor/pygments/style.py,sha256=rSCZWFpg1_DwFMXDU0nEVmAcBHpuQGf9RxvOPPQvKLQ,6420
pip/_vendor/pygments/styles/__init__.py,sha256=qUk6_1z5KmT8EdJFZYgESmG6P_HJF_2vVrDD7HSCGYY,2042
pip/_vendor/pygments/styles/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/pygments/styles/__pycache__/_mapping.cpython-313.pyc,,
pip/_vendor/pygments/styles/_mapping.py,sha256=6lovFUE29tz6EsV3XYY4hgozJ7q1JL7cfO3UOlgnS8w,3312
pip/_vendor/pygments/token.py,sha256=qZwT7LSPy5YBY3JgDjut642CCy7JdQzAfmqD9NmT5j0,6226
pip/_vendor/pygments/unistring.py,sha256=p5c1i-HhoIhWemy9CUsaN9o39oomYHNxXll0Xfw6tEA,63208
pip/_vendor/pygments/util.py,sha256=2tj2nS1X9_OpcuSjf8dOET2bDVZhs8cEKd_uT6-Fgg8,10031
pip/_vendor/pyproject_hooks/__init__.py,sha256=kCehmy0UaBa9oVMD7ZIZrnswfnP3LXZ5lvnNJAL5JBM,491
pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/pyproject_hooks/__pycache__/_compat.cpython-313.pyc,,
pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-313.pyc,,
pip/_vendor/pyproject_hooks/_compat.py,sha256=by6evrYnqkisiM-MQcvOKs5bgDMzlOSgZqRHNqf04zE,138
pip/_vendor/pyproject_hooks/_impl.py,sha256=61GJxzQip0IInhuO69ZI5GbNQ82XEDUB_1Gg5_KtUoc,11920
pip/_vendor/pyproject_hooks/_in_process/__init__.py,sha256=9gQATptbFkelkIy0OfWFEACzqxXJMQDWCH9rBOAZVwQ,546
pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-313.pyc,,
pip/_vendor/pyproject_hooks/_in_process/_in_process.py,sha256=m2b34c917IW5o-Q_6TYIHlsK9lSUlNiyrITTUH_zwew,10927
pip/_vendor/requests/__init__.py,sha256=HlB_HzhrzGtfD_aaYUwUh1zWXLZ75_YCLyit75d0Vz8,5057
pip/_vendor/requests/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/__version__.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/_internal_utils.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/adapters.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/api.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/auth.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/certs.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/compat.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/cookies.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/exceptions.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/help.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/hooks.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/models.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/packages.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/sessions.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/status_codes.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/structures.cpython-313.pyc,,
pip/_vendor/requests/__pycache__/utils.cpython-313.pyc,,
pip/_vendor/requests/__version__.py,sha256=FVfglgZmNQnmYPXpOohDU58F5EUb_-VnSTaAesS187g,435
pip/_vendor/requests/_internal_utils.py,sha256=nMQymr4hs32TqVo5AbCrmcJEhvPUh7xXlluyqwslLiQ,1495
pip/_vendor/requests/adapters.py,sha256=J7VeVxKBvawbtlX2DERVo05J9BXTcWYLMHNd1Baa-bk,27607
pip/_vendor/requests/api.py,sha256=_Zb9Oa7tzVIizTKwFrPjDEY9ejtm_OnSRERnADxGsQs,6449
pip/_vendor/requests/auth.py,sha256=kF75tqnLctZ9Mf_hm9TZIj4cQWnN5uxRz8oWsx5wmR0,10186
pip/_vendor/requests/certs.py,sha256=PVPooB0jP5hkZEULSCwC074532UFbR2Ptgu0I5zwmCs,575
pip/_vendor/requests/compat.py,sha256=Mo9f9xZpefod8Zm-n9_StJcVTmwSukXR2p3IQyyVXvU,1485
pip/_vendor/requests/cookies.py,sha256=bNi-iqEj4NPZ00-ob-rHvzkvObzN3lEpgw3g6paS3Xw,18590
pip/_vendor/requests/exceptions.py,sha256=D1wqzYWne1mS2rU43tP9CeN1G7QAy7eqL9o1god6Ejw,4272
pip/_vendor/requests/help.py,sha256=hRKaf9u0G7fdwrqMHtF3oG16RKktRf6KiwtSq2Fo1_0,3813
pip/_vendor/requests/hooks.py,sha256=CiuysiHA39V5UfcCBXFIx83IrDpuwfN9RcTUgv28ftQ,733
pip/_vendor/requests/models.py,sha256=x4K4CmH-lC0l2Kb-iPfMN4dRXxHEcbOaEWBL_i09AwI,35483
pip/_vendor/requests/packages.py,sha256=_ZQDCJTJ8SP3kVWunSqBsRZNPzj2c1WFVqbdr08pz3U,1057
pip/_vendor/requests/sessions.py,sha256=ykTI8UWGSltOfH07HKollH7kTBGw4WhiBVaQGmckTw4,30495
pip/_vendor/requests/status_codes.py,sha256=iJUAeA25baTdw-6PfD0eF4qhpINDJRJI-yaMqxs4LEI,4322
pip/_vendor/requests/structures.py,sha256=-IbmhVz06S-5aPSZuUthZ6-6D9XOjRuTXHOabY041XM,2912
pip/_vendor/requests/utils.py,sha256=L79vnFbzJ3SFLKtJwpoWe41Tozi3RlZv94pY1TFIyow,33631
pip/_vendor/resolvelib/__init__.py,sha256=h509TdEcpb5-44JonaU3ex2TM15GVBLjM9CNCPwnTTs,537
pip/_vendor/resolvelib/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/resolvelib/__pycache__/providers.cpython-313.pyc,,
pip/_vendor/resolvelib/__pycache__/reporters.cpython-313.pyc,,
pip/_vendor/resolvelib/__pycache__/resolvers.cpython-313.pyc,,
pip/_vendor/resolvelib/__pycache__/structs.cpython-313.pyc,,
pip/_vendor/resolvelib/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-313.pyc,,
pip/_vendor/resolvelib/compat/collections_abc.py,sha256=uy8xUZ-NDEw916tugUXm8HgwCGiMO0f-RcdnpkfXfOs,156
pip/_vendor/resolvelib/providers.py,sha256=fuuvVrCetu5gsxPB43ERyjfO8aReS3rFQHpDgiItbs4,5871
pip/_vendor/resolvelib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/resolvelib/reporters.py,sha256=TSbRmWzTc26w0ggsV1bxVpeWDB8QNIre6twYl7GIZBE,1601
pip/_vendor/resolvelib/resolvers.py,sha256=G8rsLZSq64g5VmIq-lB7UcIJ1gjAxIQJmTF4REZleQ0,20511
pip/_vendor/resolvelib/structs.py,sha256=0_1_XO8z_CLhegP3Vpf9VJ3zJcfLm0NOHRM-i0Ykz3o,4963
pip/_vendor/rich/__init__.py,sha256=dRxjIL-SbFVY0q3IjSMrfgBTHrm1LZDgLOygVBwiYZc,6090
pip/_vendor/rich/__main__.py,sha256=eO7Cq8JnrgG8zVoeImiAs92q3hXNMIfp0w5lMsO7Q2Y,8477
pip/_vendor/rich/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/__main__.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_cell_widths.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_emoji_codes.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_emoji_replace.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_export_format.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_extension.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_fileno.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_inspect.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_log_render.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_loop.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_null_file.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_palettes.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_pick.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_ratio.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_spinners.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_stack.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_timer.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_win32_console.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_windows.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_windows_renderer.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/_wrap.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/abc.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/align.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/ansi.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/bar.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/box.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/cells.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/color.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/color_triplet.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/columns.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/console.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/constrain.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/containers.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/control.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/default_styles.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/diagnose.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/emoji.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/errors.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/file_proxy.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/filesize.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/highlighter.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/json.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/jupyter.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/layout.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/live.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/live_render.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/logging.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/markup.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/measure.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/padding.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/pager.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/palette.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/panel.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/pretty.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/progress.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/progress_bar.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/prompt.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/protocol.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/region.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/repr.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/rule.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/scope.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/screen.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/segment.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/spinner.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/status.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/style.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/styled.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/syntax.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/table.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/terminal_theme.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/text.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/theme.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/themes.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/traceback.cpython-313.pyc,,
pip/_vendor/rich/__pycache__/tree.cpython-313.pyc,,
pip/_vendor/rich/_cell_widths.py,sha256=fbmeyetEdHjzE_Vx2l1uK7tnPOhMs2X1lJfO3vsKDpA,10209
pip/_vendor/rich/_emoji_codes.py,sha256=hu1VL9nbVdppJrVoijVshRlcRRe_v3dju3Mmd2sKZdY,140235
pip/_vendor/rich/_emoji_replace.py,sha256=n-kcetsEUx2ZUmhQrfeMNc-teeGhpuSQ5F8VPBsyvDo,1064
pip/_vendor/rich/_export_format.py,sha256=RI08pSrm5tBSzPMvnbTqbD9WIalaOoN5d4M1RTmLq1Y,2128
pip/_vendor/rich/_extension.py,sha256=Xt47QacCKwYruzjDi-gOBq724JReDj9Cm9xUi5fr-34,265
pip/_vendor/rich/_fileno.py,sha256=HWZxP5C2ajMbHryvAQZseflVfQoGzsKOHzKGsLD8ynQ,799
pip/_vendor/rich/_inspect.py,sha256=oZJGw31e64dwXSCmrDnvZbwVb1ZKhWfU8wI3VWohjJk,9695
pip/_vendor/rich/_log_render.py,sha256=1ByI0PA1ZpxZY3CGJOK54hjlq4X-Bz_boIjIqCd8Kns,3225
pip/_vendor/rich/_loop.py,sha256=hV_6CLdoPm0va22Wpw4zKqM0RYsz3TZxXj0PoS-9eDQ,1236
pip/_vendor/rich/_null_file.py,sha256=tGSXk_v-IZmbj1GAzHit8A3kYIQMiCpVsCFfsC-_KJ4,1387
pip/_vendor/rich/_palettes.py,sha256=cdev1JQKZ0JvlguV9ipHgznTdnvlIzUFDBb0It2PzjI,7063
pip/_vendor/rich/_pick.py,sha256=evDt8QN4lF5CiwrUIXlOJCntitBCOsI3ZLPEIAVRLJU,423
pip/_vendor/rich/_ratio.py,sha256=Zt58apszI6hAAcXPpgdWKpu3c31UBWebOeR4mbyptvU,5471
pip/_vendor/rich/_spinners.py,sha256=U2r1_g_1zSjsjiUdAESc2iAMc3i4ri_S8PYP6kQ5z1I,19919
pip/_vendor/rich/_stack.py,sha256=-C8OK7rxn3sIUdVwxZBBpeHhIzX0eI-VM3MemYfaXm0,351
pip/_vendor/rich/_timer.py,sha256=zelxbT6oPFZnNrwWPpc1ktUeAT-Vc4fuFcRZLQGLtMI,417
pip/_vendor/rich/_win32_console.py,sha256=P0vxI2fcndym1UU1S37XAzQzQnkyY7YqAKmxm24_gug,22820
pip/_vendor/rich/_windows.py,sha256=aBwaD_S56SbgopIvayVmpk0Y28uwY2C5Bab1wl3Bp-I,1925
pip/_vendor/rich/_windows_renderer.py,sha256=t74ZL3xuDCP3nmTp9pH1L5LiI2cakJuQRQleHCJerlk,2783
pip/_vendor/rich/_wrap.py,sha256=FlSsom5EX0LVkA3KWy34yHnCfLtqX-ZIepXKh-70rpc,3404
pip/_vendor/rich/abc.py,sha256=ON-E-ZqSSheZ88VrKX2M3PXpFbGEUUZPMa_Af0l-4f0,890
pip/_vendor/rich/align.py,sha256=sCUkisXkQfoq-IQPyBELfJ8l7LihZJX3HbH8K7Cie-M,10368
pip/_vendor/rich/ansi.py,sha256=iD6532QYqnBm6hADulKjrV8l8kFJ-9fEVooHJHH3hMg,6906
pip/_vendor/rich/bar.py,sha256=ldbVHOzKJOnflVNuv1xS7g6dLX2E3wMnXkdPbpzJTcs,3263
pip/_vendor/rich/box.py,sha256=nr5fYIUghB_iUCEq6y0Z3LlCT8gFPDrzN9u2kn7tJl4,10831
pip/_vendor/rich/cells.py,sha256=aMmGK4BjXhgE6_JF1ZEGmW3O7mKkE8g84vUnj4Et4To,4780
pip/_vendor/rich/color.py,sha256=bCRATVdRe5IClJ6Hl62de2PKQ_U4i2MZ4ugjUEg7Tao,18223
pip/_vendor/rich/color_triplet.py,sha256=3lhQkdJbvWPoLDO-AnYImAWmJvV5dlgYNCVZ97ORaN4,1054
pip/_vendor/rich/columns.py,sha256=HUX0KcMm9dsKNi11fTbiM_h2iDtl8ySCaVcxlalEzq8,7131
pip/_vendor/rich/console.py,sha256=deFZIubq2M9A2MCsKFAsFQlWDvcOMsGuUA07QkOaHIw,99173
pip/_vendor/rich/constrain.py,sha256=1VIPuC8AgtKWrcncQrjBdYqA3JVWysu6jZo1rrh7c7Q,1288
pip/_vendor/rich/containers.py,sha256=c_56TxcedGYqDepHBMTuZdUIijitAQgnox-Qde0Z1qo,5502
pip/_vendor/rich/control.py,sha256=DSkHTUQLorfSERAKE_oTAEUFefZnZp4bQb4q8rHbKws,6630
pip/_vendor/rich/default_styles.py,sha256=-Fe318kMVI_IwciK5POpThcO0-9DYJ67TZAN6DlmlmM,8082
pip/_vendor/rich/diagnose.py,sha256=an6uouwhKPAlvQhYpNNpGq9EJysfMIOvvCbO3oSoR24,972
pip/_vendor/rich/emoji.py,sha256=omTF9asaAnsM4yLY94eR_9dgRRSm1lHUszX20D1yYCQ,2501
pip/_vendor/rich/errors.py,sha256=5pP3Kc5d4QJ_c0KFsxrfyhjiPVe7J1zOqSFbFAzcV-Y,642
pip/_vendor/rich/file_proxy.py,sha256=Tl9THMDZ-Pk5Wm8sI1gGg_U5DhusmxD-FZ0fUbcU0W0,1683
pip/_vendor/rich/filesize.py,sha256=9fTLAPCAwHmBXdRv7KZU194jSgNrRb6Wx7RIoBgqeKY,2508
pip/_vendor/rich/highlighter.py,sha256=6ZAjUcNhBRajBCo9umFUclyi2xL0-55JL7S0vYGUJu4,9585
pip/_vendor/rich/json.py,sha256=vVEoKdawoJRjAFayPwXkMBPLy7RSTs-f44wSQDR2nJ0,5031
pip/_vendor/rich/jupyter.py,sha256=QyoKoE_8IdCbrtiSHp9TsTSNyTHY0FO5whE7jOTd9UE,3252
pip/_vendor/rich/layout.py,sha256=ajkSFAtEVv9EFTcFs-w4uZfft7nEXhNzL7ZVdgrT5rI,14004
pip/_vendor/rich/live.py,sha256=vUcnJV2LMSK3sQNaILbm0-_B8BpAeiHfcQMAMLfpRe0,14271
pip/_vendor/rich/live_render.py,sha256=zJtB471jGziBtEwxc54x12wEQtH4BuQr1SA8v9kU82w,3666
pip/_vendor/rich/logging.py,sha256=uB-cB-3Q4bmXDLLpbOWkmFviw-Fde39zyMV6tKJ2WHQ,11903
pip/_vendor/rich/markup.py,sha256=3euGKP5s41NCQwaSjTnJxus5iZMHjxpIM0W6fCxra38,8451
pip/_vendor/rich/measure.py,sha256=HmrIJX8sWRTHbgh8MxEay_83VkqNW_70s8aKP5ZcYI8,5305
pip/_vendor/rich/padding.py,sha256=kTFGsdGe0os7tXLnHKpwTI90CXEvrceeZGCshmJy5zw,4970
pip/_vendor/rich/pager.py,sha256=SO_ETBFKbg3n_AgOzXm41Sv36YxXAyI3_R-KOY2_uSc,828
pip/_vendor/rich/palette.py,sha256=lInvR1ODDT2f3UZMfL1grq7dY_pDdKHw4bdUgOGaM4Y,3396
pip/_vendor/rich/panel.py,sha256=2Fd1V7e1kHxlPFIusoHY5T7-Cs0RpkrihgVG9ZVqJ4g,10705
pip/_vendor/rich/pretty.py,sha256=5oIHP_CGWnHEnD0zMdW5qfGC5kHqIKn7zH_eC4crULE,35848
pip/_vendor/rich/progress.py,sha256=P02xi7T2Ua3qq17o83bkshe4c0v_45cg8VyTj6US6Vg,59715
pip/_vendor/rich/progress_bar.py,sha256=L4jw8E6Qb_x-jhOrLVhkuMaPmiAhFIl8jHQbWFrKuR8,8164
pip/_vendor/rich/prompt.py,sha256=wdOn2X8XTJKnLnlw6PoMY7xG4iUPp3ezt4O5gqvpV-E,11304
pip/_vendor/rich/protocol.py,sha256=5hHHDDNHckdk8iWH5zEbi-zuIVSF5hbU2jIo47R7lTE,1391
pip/_vendor/rich/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/rich/region.py,sha256=rNT9xZrVZTYIXZC0NYn41CJQwYNbR-KecPOxTgQvB8Y,166
pip/_vendor/rich/repr.py,sha256=5MZJZmONgC6kud-QW-_m1okXwL2aR6u6y-pUcUCJz28,4431
pip/_vendor/rich/rule.py,sha256=0fNaS_aERa3UMRc3T5WMpN_sumtDxfaor2y3of1ftBk,4602
pip/_vendor/rich/scope.py,sha256=TMUU8qo17thyqQCPqjDLYpg_UU1k5qVd-WwiJvnJVas,2843
pip/_vendor/rich/screen.py,sha256=YoeReESUhx74grqb0mSSb9lghhysWmFHYhsbMVQjXO8,1591
pip/_vendor/rich/segment.py,sha256=hU1ueeXqI6YeFa08K9DAjlF2QLxcJY9pwZx7RsXavlk,24246
pip/_vendor/rich/spinner.py,sha256=15koCmF0DQeD8-k28Lpt6X_zJQUlzEhgo_6A6uy47lc,4339
pip/_vendor/rich/status.py,sha256=kkPph3YeAZBo-X-4wPp8gTqZyU466NLwZBA4PZTTewo,4424
pip/_vendor/rich/style.py,sha256=3hiocH_4N8vwRm3-8yFWzM7tSwjjEven69XqWasSQwM,27073
pip/_vendor/rich/styled.py,sha256=eZNnzGrI4ki_54pgY3Oj0T-x3lxdXTYh4_ryDB24wBU,1258
pip/_vendor/rich/syntax.py,sha256=TnZDuOD4DeHFbkaVEAji1gf8qgAlMU9Boe_GksMGCkk,35475
pip/_vendor/rich/table.py,sha256=nGEvAZHF4dy1vT9h9Gj9O5qhSQO3ODAxJv0RY1vnIB8,39680
pip/_vendor/rich/terminal_theme.py,sha256=1j5-ufJfnvlAo5Qsi_ACZiXDmwMXzqgmFByObT9-yJY,3370
pip/_vendor/rich/text.py,sha256=5rQ3zvNrg5UZKNLecbh7fiw9v3HeFulNVtRY_CBDjjE,47312
pip/_vendor/rich/theme.py,sha256=belFJogzA0W0HysQabKaHOc3RWH2ko3fQAJhoN-AFdo,3777
pip/_vendor/rich/themes.py,sha256=0xgTLozfabebYtcJtDdC5QkX5IVUEaviqDUJJh4YVFk,102
pip/_vendor/rich/traceback.py,sha256=CUpxYLjQWIb6vQQ6O72X0hvDV6caryGqU6UweHgOyCY,29601
pip/_vendor/rich/tree.py,sha256=meAOUU6sYnoBEOX2ILrPLY9k5bWrWNQKkaiEFvHinXM,9167
pip/_vendor/tomli/__init__.py,sha256=JhUwV66DB1g4Hvt1UQCVMdfCu-IgAV8FXmvDU9onxd4,396
pip/_vendor/tomli/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/tomli/__pycache__/_parser.cpython-313.pyc,,
pip/_vendor/tomli/__pycache__/_re.cpython-313.pyc,,
pip/_vendor/tomli/__pycache__/_types.cpython-313.pyc,,
pip/_vendor/tomli/_parser.py,sha256=g9-ENaALS-B8dokYpCuzUFalWlog7T-SIYMjLZSWrtM,22633
pip/_vendor/tomli/_re.py,sha256=dbjg5ChZT23Ka9z9DHOXfdtSpPwUfdgMXnj8NOoly-w,2943
pip/_vendor/tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQcTi3Q,254
pip/_vendor/tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
pip/_vendor/truststore/__init__.py,sha256=WIDeyzWm7EVX44g354M25vpRXbeY1lsPH6EmUJUcq4o,1264
pip/_vendor/truststore/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/truststore/__pycache__/_api.cpython-313.pyc,,
pip/_vendor/truststore/__pycache__/_macos.cpython-313.pyc,,
pip/_vendor/truststore/__pycache__/_openssl.cpython-313.pyc,,
pip/_vendor/truststore/__pycache__/_ssl_constants.cpython-313.pyc,,
pip/_vendor/truststore/__pycache__/_windows.cpython-313.pyc,,
pip/_vendor/truststore/_api.py,sha256=GeXRNTlxPZ3kif4kNoh6JY0oE4QRzTGcgXr6l_X_Gk0,10555
pip/_vendor/truststore/_macos.py,sha256=nZlLkOmszUE0g6ryRwBVGY5COzPyudcsiJtDWarM5LQ,20503
pip/_vendor/truststore/_openssl.py,sha256=LLUZ7ZGaio-i5dpKKjKCSeSufmn6T8pi9lDcFnvSyq0,2324
pip/_vendor/truststore/_ssl_constants.py,sha256=NUD4fVKdSD02ri7-db0tnO0VqLP9aHuzmStcW7tAl08,1130
pip/_vendor/truststore/_windows.py,sha256=rAHyKYD8M7t-bXfG8VgOVa3TpfhVhbt4rZQlO45YuP8,17993
pip/_vendor/truststore/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/typing_extensions.py,sha256=78hFl0HpDY-ylHUVCnWdU5nTHxUP2-S-3wEZk6CQmLk,134499
pip/_vendor/urllib3/__init__.py,sha256=iXLcYiJySn0GNbWOOZDDApgBL1JgP44EZ8i1760S8Mc,3333
pip/_vendor/urllib3/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/urllib3/__pycache__/_collections.cpython-313.pyc,,
pip/_vendor/urllib3/__pycache__/_version.cpython-313.pyc,,
pip/_vendor/urllib3/__pycache__/connection.cpython-313.pyc,,
pip/_vendor/urllib3/__pycache__/connectionpool.cpython-313.pyc,,
pip/_vendor/urllib3/__pycache__/exceptions.cpython-313.pyc,,
pip/_vendor/urllib3/__pycache__/fields.cpython-313.pyc,,
pip/_vendor/urllib3/__pycache__/filepost.cpython-313.pyc,,
pip/_vendor/urllib3/__pycache__/poolmanager.cpython-313.pyc,,
pip/_vendor/urllib3/__pycache__/request.cpython-313.pyc,,
pip/_vendor/urllib3/__pycache__/response.cpython-313.pyc,,
pip/_vendor/urllib3/_collections.py,sha256=pyASJJhW7wdOpqJj9QJA8FyGRfr8E8uUUhqUvhF0728,11372
pip/_vendor/urllib3/_version.py,sha256=t9wGB6ooOTXXgiY66K1m6BZS1CJyXHAU8EoWDTe6Shk,64
pip/_vendor/urllib3/connection.py,sha256=ttIA909BrbTUzwkqEe_TzZVh4JOOj7g61Ysei2mrwGg,20314
pip/_vendor/urllib3/connectionpool.py,sha256=e2eiAwNbFNCKxj4bwDKNK-w7HIdSz3OmMxU_TIt-evQ,40408
pip/_vendor/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-313.pyc,,
pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-313.pyc,,
pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-313.pyc,,
pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-313.pyc,,
pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-313.pyc,,
pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-313.pyc,,
pip/_vendor/urllib3/contrib/_appengine_environ.py,sha256=bDbyOEhW2CKLJcQqAKAyrEHN-aklsyHFKq6vF8ZFsmk,957
pip/_vendor/urllib3/contrib/_securetransport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-313.pyc,,
pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-313.pyc,,
pip/_vendor/urllib3/contrib/_securetransport/bindings.py,sha256=4Xk64qIkPBt09A5q-RIFUuDhNc9mXilVapm7WnYnzRw,17632
pip/_vendor/urllib3/contrib/_securetransport/low_level.py,sha256=B2JBB2_NRP02xK6DCa1Pa9IuxrPwxzDzZbixQkb7U9M,13922
pip/_vendor/urllib3/contrib/appengine.py,sha256=VR68eAVE137lxTgjBDwCna5UiBZTOKa01Aj_-5BaCz4,11036
pip/_vendor/urllib3/contrib/ntlmpool.py,sha256=NlfkW7WMdW8ziqudopjHoW299og1BTWi0IeIibquFwk,4528
pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=hDJh4MhyY_p-oKlFcYcQaVQRDv6GMmBGuW9yjxyeejM,17081
pip/_vendor/urllib3/contrib/securetransport.py,sha256=Fef1IIUUFHqpevzXiDPbIGkDKchY2FVKeVeLGR1Qq3g,34446
pip/_vendor/urllib3/contrib/socks.py,sha256=aRi9eWXo9ZEb95XUxef4Z21CFlnnjbEiAo9HOseoMt4,7097
pip/_vendor/urllib3/exceptions.py,sha256=0Mnno3KHTNfXRfY7638NufOPkUb6mXOm-Lqj-4x2w8A,8217
pip/_vendor/urllib3/fields.py,sha256=kvLDCg_JmH1lLjUUEY_FLS8UhY7hBvDPuVETbY8mdrM,8579
pip/_vendor/urllib3/filepost.py,sha256=5b_qqgRHVlL7uLtdAYBzBh-GHmU5AfJVt_2N0XS3PeY,2440
pip/_vendor/urllib3/packages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/urllib3/packages/__pycache__/six.cpython-313.pyc,,
pip/_vendor/urllib3/packages/backports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-313.pyc,,
pip/_vendor/urllib3/packages/backports/__pycache__/weakref_finalize.cpython-313.pyc,,
pip/_vendor/urllib3/packages/backports/makefile.py,sha256=nbzt3i0agPVP07jqqgjhaYjMmuAi_W5E0EywZivVO8E,1417
pip/_vendor/urllib3/packages/backports/weakref_finalize.py,sha256=tRCal5OAhNSRyb0DhHp-38AtIlCsRP8BxF3NX-6rqIA,5343
pip/_vendor/urllib3/packages/six.py,sha256=b9LM0wBXv7E7SrbCjAm4wwN-hrH-iNxv18LgWNMMKPo,34665
pip/_vendor/urllib3/poolmanager.py,sha256=aWyhXRtNO4JUnCSVVqKTKQd8EXTvUm1VN9pgs2bcONo,19990
pip/_vendor/urllib3/request.py,sha256=YTWFNr7QIwh7E1W9dde9LM77v2VWTJ5V78XuTTw7D1A,6691
pip/_vendor/urllib3/response.py,sha256=fmDJAFkG71uFTn-sVSTh2Iw0WmcXQYqkbRjihvwBjU8,30641
pip/_vendor/urllib3/util/__init__.py,sha256=JEmSmmqqLyaw8P51gUImZh8Gwg9i1zSe-DoqAitn2nc,1155
pip/_vendor/urllib3/util/__pycache__/__init__.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/connection.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/proxy.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/queue.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/request.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/response.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/retry.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/timeout.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/url.cpython-313.pyc,,
pip/_vendor/urllib3/util/__pycache__/wait.cpython-313.pyc,,
pip/_vendor/urllib3/util/connection.py,sha256=5Lx2B1PW29KxBn2T0xkN1CBgRBa3gGVJBKoQoRogEVk,4901
pip/_vendor/urllib3/util/proxy.py,sha256=zUvPPCJrp6dOF0N4GAVbOcl6o-4uXKSrGiTkkr5vUS4,1605
pip/_vendor/urllib3/util/queue.py,sha256=nRgX8_eX-_VkvxoX096QWoz8Ps0QHUAExILCY_7PncM,498
pip/_vendor/urllib3/util/request.py,sha256=C0OUt2tcU6LRiQJ7YYNP9GvPrSvl7ziIBekQ-5nlBZk,3997
pip/_vendor/urllib3/util/response.py,sha256=GJpg3Egi9qaJXRwBh5wv-MNuRWan5BIu40oReoxWP28,3510
pip/_vendor/urllib3/util/retry.py,sha256=6ENvOZ8PBDzh8kgixpql9lIrb2dxH-k7ZmBanJF2Ng4,22050
pip/_vendor/urllib3/util/ssl_.py,sha256=QDuuTxPSCj1rYtZ4xpD7Ux-r20TD50aHyqKyhQ7Bq4A,17460
pip/_vendor/urllib3/util/ssl_match_hostname.py,sha256=Ir4cZVEjmAk8gUAIHWSi7wtOO83UCYABY2xFD1Ql_WA,5758
pip/_vendor/urllib3/util/ssltransport.py,sha256=NA-u5rMTrDFDFC8QzRKUEKMG0561hOD4qBTr3Z4pv6E,6895
pip/_vendor/urllib3/util/timeout.py,sha256=cwq4dMk87mJHSBktK1miYJ-85G-3T3RmT20v7SFCpno,10168
pip/_vendor/urllib3/util/url.py,sha256=lCAE7M5myA8EDdW0sJuyyZhVB9K_j38ljWhHAnFaWoE,14296
pip/_vendor/urllib3/util/wait.py,sha256=fOX0_faozG2P7iVojQoE1mbydweNyTcm-hXEfFrTtLI,5403
pip/_vendor/vendor.txt,sha256=43152uDtpsunEE29vmLqqKZUosdrbvzIFkzscLB55Cg,332
pip/py.typed,sha256=EBVvvPRTn_eIpz5e5QztSCdrMX7Qwd7VP93RSoIlZ2I,286

View File

@ -1,5 +0,0 @@
Wheel-Version: 1.0
Generator: setuptools (75.2.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@ -1,3 +0,0 @@
[console_scripts]
pip = pip._internal.cli.main:main
pip3 = pip._internal.cli.main:main

View File

@ -1,6 +1,6 @@
from typing import List, Optional
__version__ = "24.3.1"
__version__ = "25.0.1"
def main(args: Optional[List[str]] = None) -> int:

View File

@ -11,7 +11,6 @@ from collections import OrderedDict
from types import TracebackType
from typing import TYPE_CHECKING, Iterable, List, Optional, Set, Tuple, Type, Union
from pip._vendor.certifi import where
from pip._vendor.packaging.version import Version
from pip import __file__ as pip_location
@ -270,21 +269,25 @@ class BuildEnvironment:
for link in finder.find_links:
args.extend(["--find-links", link])
if finder.proxy:
args.extend(["--proxy", finder.proxy])
for host in finder.trusted_hosts:
args.extend(["--trusted-host", host])
if finder.custom_cert:
args.extend(["--cert", finder.custom_cert])
if finder.client_cert:
args.extend(["--client-cert", finder.client_cert])
if finder.allow_all_prereleases:
args.append("--pre")
if finder.prefer_binary:
args.append("--prefer-binary")
args.append("--")
args.extend(requirements)
extra_environ = {"_PIP_STANDALONE_CERT": where()}
with open_spinner(f"Installing {kind}") as spinner:
call_subprocess(
args,
command_desc=f"pip subprocess to install {kind}",
spinner=spinner,
extra_environ=extra_environ,
)

View File

@ -29,6 +29,7 @@ from pip._internal.exceptions import (
NetworkConnectionError,
PreviousBuildDirError,
)
from pip._internal.utils.deprecation import deprecated
from pip._internal.utils.filesystem import check_path_owner
from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging
from pip._internal.utils.misc import get_prog, normalize_path
@ -228,4 +229,12 @@ class Command(CommandContextMixIn):
)
options.cache_dir = None
if options.no_python_version_warning:
deprecated(
reason="--no-python-version-warning is deprecated.",
replacement="to remove the flag as it's a no-op",
gone_in="25.1",
issue=13154,
)
return self._run_wrapper(level_number, options, args)

View File

@ -260,8 +260,8 @@ keyring_provider: Callable[..., Option] = partial(
default="auto",
help=(
"Enable the credential lookup via the keyring library if user input is allowed."
" Specify which mechanism to use [disabled, import, subprocess]."
" (default: disabled)"
" Specify which mechanism to use [auto, disabled, import, subprocess]."
" (default: %default)"
),
)

View File

@ -123,6 +123,7 @@ class SessionCommandMixin(CommandContextMixIn):
"https": options.proxy,
}
session.trust_env = False
session.pip_proxy = options.proxy
# Determine if we can prompt the user for authentication or not
session.auth.prompting = not options.no_input

View File

@ -63,7 +63,7 @@ def _raw_progress_bar(
size: Optional[int],
) -> Generator[bytes, None, None]:
def write_progress(current: int, total: int) -> None:
sys.stdout.write("Progress %d of %d\n" % (current, total))
sys.stdout.write(f"Progress {current} of {total}\n")
sys.stdout.flush()
current = 0

View File

@ -8,6 +8,7 @@ from pip._internal.cli.status_codes import ERROR, SUCCESS
from pip._internal.exceptions import CommandError, PipError
from pip._internal.utils import filesystem
from pip._internal.utils.logging import getLogger
from pip._internal.utils.misc import format_size
logger = getLogger(__name__)
@ -180,10 +181,12 @@ class CacheCommand(Command):
if not files:
logger.warning(no_matching_msg)
bytes_removed = 0
for filename in files:
bytes_removed += os.stat(filename).st_size
os.unlink(filename)
logger.verbose("Removed %s", filename)
logger.info("Files removed: %s", len(files))
logger.info("Files removed: %s (%s)", len(files), format_size(bytes_removed))
def purge_cache(self, options: Values, args: List[Any]) -> None:
if args:

View File

@ -10,6 +10,13 @@ from typing import List, Optional
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.rich import print_json
# Eagerly import self_outdated_check to avoid crashes. Otherwise,
# this module would be imported *after* pip was replaced, resulting
# in crashes if the new self_outdated_check module was incompatible
# with the rest of pip that's already imported, or allowing a
# wheel to execute arbitrary code on install by replacing
# self_outdated_check.
import pip._internal.self_outdated_check # noqa: F401
from pip._internal.cache import WheelCache
from pip._internal.cli import cmdoptions
from pip._internal.cli.cmdoptions import make_target_python
@ -408,12 +415,6 @@ class InstallCommand(RequirementCommand):
# If we're not replacing an already installed pip,
# we're not modifying it.
modifying_pip = pip_req.satisfied_by is None
if modifying_pip:
# Eagerly import this module to avoid crashes. Otherwise, this
# module would be imported *after* pip was replaced, resulting in
# crashes if the new self_outdated_check module was incompatible
# with the rest of pip that's already imported.
import pip._internal.self_outdated_check # noqa: F401
protect_pip_from_modification_on_windows(modifying_pip=modifying_pip)
reqs_to_build = [
@ -432,7 +433,7 @@ class InstallCommand(RequirementCommand):
if build_failures:
raise InstallationError(
"ERROR: Failed to build installable wheels for some "
"Failed to build installable wheels for some "
"pyproject.toml based projects ({})".format(
", ".join(r.name for r in build_failures) # type: ignore
)

View File

@ -66,6 +66,7 @@ class _PackageInfo(NamedTuple):
author: str
author_email: str
license: str
license_expression: str
entry_points: List[str]
files: Optional[List[str]]
@ -161,6 +162,7 @@ def search_packages_info(query: List[str]) -> Generator[_PackageInfo, None, None
author=metadata.get("Author", ""),
author_email=metadata.get("Author-email", ""),
license=metadata.get("License", ""),
license_expression=metadata.get("License-Expression", ""),
entry_points=entry_points,
files=files,
)
@ -180,13 +182,18 @@ def print_results(
if i > 0:
write_output("---")
metadata_version_tuple = tuple(map(int, dist.metadata_version.split(".")))
write_output("Name: %s", dist.name)
write_output("Version: %s", dist.version)
write_output("Summary: %s", dist.summary)
write_output("Home-page: %s", dist.homepage)
write_output("Author: %s", dist.author)
write_output("Author-email: %s", dist.author_email)
write_output("License: %s", dist.license)
if metadata_version_tuple >= (2, 4) and dist.license_expression:
write_output("License-Expression: %s", dist.license_expression)
else:
write_output("License: %s", dist.license)
write_output("Location: %s", dist.location)
if dist.editable_project_location is not None:
write_output(

View File

@ -330,7 +330,7 @@ class Configuration:
This should be treated like items of a dictionary. The order
here doesn't affect what gets overridden. That is controlled
by OVERRIDE_ORDER. However this does control the order they are
displayed to the user. It's probably most ergononmic to display
displayed to the user. It's probably most ergonomic to display
things in the same order as OVERRIDE_ORDER
"""
# SMELL: Move the conditions out of this function

View File

@ -334,44 +334,30 @@ class CandidatePreferences:
allow_all_prereleases: bool = False
@dataclass(frozen=True)
class BestCandidateResult:
"""A collection of candidates, returned by `PackageFinder.find_best_candidate`.
This class is only intended to be instantiated by CandidateEvaluator's
`compute_best_candidate()` method.
:param all_candidates: A sequence of all available candidates found.
:param applicable_candidates: The applicable candidates.
:param best_candidate: The most preferred candidate found, or None
if no applicable candidates were found.
"""
def __init__(
self,
candidates: List[InstallationCandidate],
applicable_candidates: List[InstallationCandidate],
best_candidate: Optional[InstallationCandidate],
) -> None:
"""
:param candidates: A sequence of all available candidates found.
:param applicable_candidates: The applicable candidates.
:param best_candidate: The most preferred candidate found, or None
if no applicable candidates were found.
"""
assert set(applicable_candidates) <= set(candidates)
all_candidates: List[InstallationCandidate]
applicable_candidates: List[InstallationCandidate]
best_candidate: Optional[InstallationCandidate]
if best_candidate is None:
assert not applicable_candidates
def __post_init__(self) -> None:
assert set(self.applicable_candidates) <= set(self.all_candidates)
if self.best_candidate is None:
assert not self.applicable_candidates
else:
assert best_candidate in applicable_candidates
self._applicable_candidates = applicable_candidates
self._candidates = candidates
self.best_candidate = best_candidate
def iter_all(self) -> Iterable[InstallationCandidate]:
"""Iterate through all candidates."""
return iter(self._candidates)
def iter_applicable(self) -> Iterable[InstallationCandidate]:
"""Iterate through the applicable candidates."""
return iter(self._applicable_candidates)
assert self.best_candidate in self.applicable_candidates
class CandidateEvaluator:
@ -675,11 +661,29 @@ class PackageFinder:
def index_urls(self) -> List[str]:
return self.search_scope.index_urls
@property
def proxy(self) -> Optional[str]:
return self._link_collector.session.pip_proxy
@property
def trusted_hosts(self) -> Iterable[str]:
for host_port in self._link_collector.session.pip_trusted_origins:
yield build_netloc(*host_port)
@property
def custom_cert(self) -> Optional[str]:
# session.verify is either a boolean (use default bundle/no SSL
# verification) or a string path to a custom CA bundle to use. We only
# care about the latter.
verify = self._link_collector.session.verify
return verify if isinstance(verify, str) else None
@property
def client_cert(self) -> Optional[str]:
cert = self._link_collector.session.cert
assert not isinstance(cert, tuple), "pip only supports PEM client certs"
return cert
@property
def allow_all_prereleases(self) -> bool:
return self._candidate_prefs.allow_all_prereleases
@ -732,6 +736,11 @@ class PackageFinder:
return no_eggs + eggs
def _log_skipped_link(self, link: Link, result: LinkType, detail: str) -> None:
# This is a hot method so don't waste time hashing links unless we're
# actually going to log 'em.
if not logger.isEnabledFor(logging.DEBUG):
return
entry = (link, result, detail)
if entry not in self._logged_links:
# Put the link at the end so the reason is more visible and because
@ -929,7 +938,7 @@ class PackageFinder:
"Could not find a version that satisfies the requirement %s "
"(from versions: %s)",
req,
_format_versions(best_candidate_result.iter_all()),
_format_versions(best_candidate_result.all_candidates),
)
raise DistributionNotFound(f"No matching distribution found for {req}")
@ -963,7 +972,7 @@ class PackageFinder:
logger.debug(
"Using version %s (newest of versions: %s)",
best_candidate.version,
_format_versions(best_candidate_result.iter_applicable()),
_format_versions(best_candidate_result.applicable_candidates),
)
return best_candidate
@ -971,7 +980,7 @@ class PackageFinder:
logger.debug(
"Installed version (%s) is most up-to-date (past versions: %s)",
installed_version,
_format_versions(best_candidate_result.iter_applicable()),
_format_versions(best_candidate_result.applicable_candidates),
)
raise BestVersionAlreadyInstalled

View File

@ -30,7 +30,7 @@ def _should_use_importlib_metadata() -> bool:
"""Whether to use the ``importlib.metadata`` or ``pkg_resources`` backend.
By default, pip uses ``importlib.metadata`` on Python 3.11+, and
``pkg_resourcess`` otherwise. This can be overridden by a couple of ways:
``pkg_resources`` otherwise. This can be overridden by a couple of ways:
* If environment variable ``_PIP_USE_IMPORTLIB_METADATA`` is set, it
dictates whether ``importlib.metadata`` is used, regardless of Python
@ -71,7 +71,7 @@ def get_default_environment() -> BaseEnvironment:
This returns an Environment instance from the chosen backend. The default
Environment instance should be built from ``sys.path`` and may use caching
to share instance state accorss calls.
to share instance state across calls.
"""
return select_backend().Environment.default()

View File

@ -23,6 +23,8 @@ METADATA_FIELDS = [
("Maintainer", False),
("Maintainer-email", False),
("License", False),
("License-Expression", False),
("License-File", True),
("Classifier", True),
("Requires-Dist", True),
("Requires-Python", False),

View File

@ -2,6 +2,7 @@ import email.message
import importlib.metadata
import pathlib
import zipfile
from os import PathLike
from typing import (
Collection,
Dict,
@ -10,6 +11,7 @@ from typing import (
Mapping,
Optional,
Sequence,
Union,
cast,
)
@ -95,6 +97,11 @@ class WheelDistribution(importlib.metadata.Distribution):
raise UnsupportedWheel(error)
return text
def locate_file(self, path: Union[str, "PathLike[str]"]) -> pathlib.Path:
# This method doesn't make sense for our in-memory wheel, but the API
# requires us to define it.
raise NotImplementedError
class Distribution(BaseDistribution):
def __init__(
@ -190,7 +197,7 @@ class Distribution(BaseDistribution):
return content
def iter_entry_points(self) -> Iterable[BaseEntryPoint]:
# importlib.metadata's EntryPoint structure sasitfies BaseEntryPoint.
# importlib.metadata's EntryPoint structure satisfies BaseEntryPoint.
return self._dist.entry_points
def _metadata_impl(self) -> email.message.Message:

View File

@ -170,12 +170,23 @@ def _ensure_quoted_url(url: str) -> str:
and without double-quoting other characters.
"""
# Split the URL into parts according to the general structure
# `scheme://netloc/path;parameters?query#fragment`.
result = urllib.parse.urlparse(url)
# `scheme://netloc/path?query#fragment`.
result = urllib.parse.urlsplit(url)
# If the netloc is empty, then the URL refers to a local filesystem path.
is_local_path = not result.netloc
path = _clean_url_path(result.path, is_local_path=is_local_path)
return urllib.parse.urlunparse(result._replace(path=path))
return urllib.parse.urlunsplit(result._replace(path=path))
def _absolute_link_url(base_url: str, url: str) -> str:
"""
A faster implementation of urllib.parse.urljoin with a shortcut
for absolute http/https URLs.
"""
if url.startswith(("https://", "http://")):
return url
else:
return urllib.parse.urljoin(base_url, url)
@functools.total_ordering
@ -185,6 +196,7 @@ class Link:
__slots__ = [
"_parsed_url",
"_url",
"_path",
"_hashes",
"comes_from",
"requires_python",
@ -241,6 +253,8 @@ class Link:
# Store the url as a private attribute to prevent accidentally
# trying to set a new value.
self._url = url
# The .path property is hot, so calculate its value ahead of time.
self._path = urllib.parse.unquote(self._parsed_url.path)
link_hash = LinkHash.find_hash_url_fragment(url)
hashes_from_link = {} if link_hash is None else link_hash.as_dict()
@ -270,7 +284,7 @@ class Link:
if file_url is None:
return None
url = _ensure_quoted_url(urllib.parse.urljoin(page_url, file_url))
url = _ensure_quoted_url(_absolute_link_url(page_url, file_url))
pyrequire = file_data.get("requires-python")
yanked_reason = file_data.get("yanked")
hashes = file_data.get("hashes", {})
@ -322,7 +336,7 @@ class Link:
if not href:
return None
url = _ensure_quoted_url(urllib.parse.urljoin(base_url, href))
url = _ensure_quoted_url(_absolute_link_url(base_url, href))
pyrequire = anchor_attribs.get("data-requires-python")
yanked_reason = anchor_attribs.get("data-yanked")
@ -421,7 +435,7 @@ class Link:
@property
def path(self) -> str:
return urllib.parse.unquote(self._parsed_url.path)
return self._path
def splitext(self) -> Tuple[str, str]:
return splitext(posixpath.basename(self.path.rstrip("/")))
@ -452,10 +466,10 @@ class Link:
project_name = match.group(1)
if not self._project_name_re.match(project_name):
deprecated(
reason=f"{self} contains an egg fragment with a non-PEP 508 name",
reason=f"{self} contains an egg fragment with a non-PEP 508 name.",
replacement="to use the req @ url syntax, and remove the egg fragment",
gone_in="25.0",
issue=11617,
gone_in="25.1",
issue=13157,
)
return project_name

View File

@ -76,6 +76,18 @@ class SafeFileCache(SeparateBodyBaseCache):
with adjacent_tmp_file(path) as f:
f.write(data)
# Inherit the read/write permissions of the cache directory
# to enable multi-user cache use-cases.
mode = (
os.stat(self.directory).st_mode
& 0o666 # select read/write permissions of cache directory
| 0o600 # set owner read/write permissions
)
# Change permissions only if there is no risk of following a symlink.
if os.chmod in os.supports_fd:
os.chmod(f.fileno(), mode)
elif os.chmod in os.supports_follow_symlinks:
os.chmod(f.name, mode, follow_symlinks=False)
replace(f.name, path)

View File

@ -339,6 +339,7 @@ class PipSession(requests.Session):
# Namespace the attribute with "pip_" just in case to prevent
# possible conflicts with the base class.
self.pip_trusted_origins: List[Tuple[str, Optional[int]]] = []
self.pip_proxy = None
# Attach our User Agent to the request
self.headers["User-Agent"] = user_agent()

View File

@ -38,4 +38,5 @@ def generate_editable_metadata(
except InstallationSubprocessError as error:
raise MetadataGenerationFailed(package_details=details) from error
assert distinfo_dir is not None
return os.path.join(metadata_dir, distinfo_dir)

View File

@ -1,9 +1,10 @@
import collections
import logging
import os
from dataclasses import dataclass, field
from typing import Container, Dict, Generator, Iterable, List, NamedTuple, Optional, Set
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
from pip._vendor.packaging.version import InvalidVersion
from pip._internal.exceptions import BadCommand, InstallationError
@ -220,19 +221,16 @@ def _get_editable_info(dist: BaseDistribution) -> _EditableInfo:
)
@dataclass(frozen=True)
class FrozenRequirement:
def __init__(
self,
name: str,
req: str,
editable: bool,
comments: Iterable[str] = (),
) -> None:
self.name = name
self.canonical_name = canonicalize_name(name)
self.req = req
self.editable = editable
self.comments = comments
name: str
req: str
editable: bool
comments: Iterable[str] = field(default_factory=tuple)
@property
def canonical_name(self) -> NormalizedName:
return canonicalize_name(self.name)
@classmethod
def from_dist(cls, dist: BaseDistribution) -> "FrozenRequirement":

View File

@ -73,7 +73,7 @@ def load_pyproject_toml(
build_system = None
# The following cases must use PEP 517
# We check for use_pep517 being non-None and falsey because that means
# We check for use_pep517 being non-None and falsy because that means
# the user explicitly requested --no-use-pep517. The value 0 as
# opposed to False can occur when the value is provided via an
# environment variable or config file option (due to the quirk of

View File

@ -2,12 +2,16 @@
Requirements file parsing
"""
import codecs
import locale
import logging
import optparse
import os
import re
import shlex
import sys
import urllib.parse
from dataclasses import dataclass
from optparse import Values
from typing import (
TYPE_CHECKING,
@ -25,7 +29,6 @@ from typing import (
from pip._internal.cli import cmdoptions
from pip._internal.exceptions import InstallationError, RequirementsFileParseError
from pip._internal.models.search_scope import SearchScope
from pip._internal.utils.encoding import auto_decode
if TYPE_CHECKING:
from pip._internal.index.package_finder import PackageFinder
@ -81,52 +84,66 @@ SUPPORTED_OPTIONS_EDITABLE_REQ_DEST = [
str(o().dest) for o in SUPPORTED_OPTIONS_EDITABLE_REQ
]
# order of BOMS is important: codecs.BOM_UTF16_LE is a prefix of codecs.BOM_UTF32_LE
# so data.startswith(BOM_UTF16_LE) would be true for UTF32_LE data
BOMS: List[Tuple[bytes, str]] = [
(codecs.BOM_UTF8, "utf-8"),
(codecs.BOM_UTF32, "utf-32"),
(codecs.BOM_UTF32_BE, "utf-32-be"),
(codecs.BOM_UTF32_LE, "utf-32-le"),
(codecs.BOM_UTF16, "utf-16"),
(codecs.BOM_UTF16_BE, "utf-16-be"),
(codecs.BOM_UTF16_LE, "utf-16-le"),
]
PEP263_ENCODING_RE = re.compile(rb"coding[:=]\s*([-\w.]+)")
DEFAULT_ENCODING = "utf-8"
logger = logging.getLogger(__name__)
@dataclass(frozen=True)
class ParsedRequirement:
def __init__(
self,
requirement: str,
is_editable: bool,
comes_from: str,
constraint: bool,
options: Optional[Dict[str, Any]] = None,
line_source: Optional[str] = None,
) -> None:
self.requirement = requirement
self.is_editable = is_editable
self.comes_from = comes_from
self.options = options
self.constraint = constraint
self.line_source = line_source
# TODO: replace this with slots=True when dropping Python 3.9 support.
__slots__ = (
"requirement",
"is_editable",
"comes_from",
"constraint",
"options",
"line_source",
)
requirement: str
is_editable: bool
comes_from: str
constraint: bool
options: Optional[Dict[str, Any]]
line_source: Optional[str]
@dataclass(frozen=True)
class ParsedLine:
def __init__(
self,
filename: str,
lineno: int,
args: str,
opts: Values,
constraint: bool,
) -> None:
self.filename = filename
self.lineno = lineno
self.opts = opts
self.constraint = constraint
__slots__ = ("filename", "lineno", "args", "opts", "constraint")
if args:
self.is_requirement = True
self.is_editable = False
self.requirement = args
elif opts.editables:
self.is_requirement = True
self.is_editable = True
filename: str
lineno: int
args: str
opts: Values
constraint: bool
@property
def is_editable(self) -> bool:
return bool(self.opts.editables)
@property
def requirement(self) -> Optional[str]:
if self.args:
return self.args
elif self.is_editable:
# We don't support multiple -e on one line
self.requirement = opts.editables[0]
else:
self.is_requirement = False
return self.opts.editables[0]
return None
def parse_requirements(
@ -179,7 +196,7 @@ def handle_requirement_line(
line.lineno,
)
assert line.is_requirement
assert line.requirement is not None
# get the options that apply to requirements
if line.is_editable:
@ -301,7 +318,7 @@ def handle_line(
affect the finder.
"""
if line.is_requirement:
if line.requirement is not None:
parsed_req = handle_requirement_line(line, options)
return parsed_req
else:
@ -340,7 +357,7 @@ class RequirementsFileParser:
parsed_files_stack: List[Dict[str, Optional[str]]],
) -> Generator[ParsedLine, None, None]:
for line in self._parse_file(filename, constraint):
if not line.is_requirement and (
if line.requirement is None and (
line.opts.requirements or line.opts.constraints
):
# parse a nested requirements file
@ -568,7 +585,39 @@ def get_file_content(url: str, session: "PipSession") -> Tuple[str, str]:
# Assume this is a bare path.
try:
with open(url, "rb") as f:
content = auto_decode(f.read())
raw_content = f.read()
except OSError as exc:
raise InstallationError(f"Could not open requirements file: {exc}")
content = _decode_req_file(raw_content, url)
return url, content
def _decode_req_file(data: bytes, url: str) -> str:
for bom, encoding in BOMS:
if data.startswith(bom):
return data[len(bom) :].decode(encoding)
for line in data.split(b"\n")[:2]:
if line[0:1] == b"#":
result = PEP263_ENCODING_RE.search(line)
if result is not None:
encoding = result.groups()[0].decode("ascii")
return data.decode(encoding)
try:
return data.decode(DEFAULT_ENCODING)
except UnicodeDecodeError:
locale_encoding = locale.getpreferredencoding(False) or sys.getdefaultencoding()
logging.warning(
"unable to decode data from %s with default encoding %s, "
"falling back to encoding from locale: %s. "
"If this is intentional you should specify the encoding with a "
"PEP-263 style comment, e.g. '# -*- coding: %s -*-'",
url,
DEFAULT_ENCODING,
locale_encoding,
locale_encoding,
)
return data.decode(locale_encoding)

View File

@ -837,7 +837,7 @@ class InstallRequirement:
"try using --config-settings editable_mode=compat. "
"Please consult the setuptools documentation for more information"
),
gone_in="25.0",
gone_in="25.1",
issue=11457,
)
if self.config_settings:
@ -925,7 +925,7 @@ def check_legacy_setup_py_options(
reason="--build-option and --global-option are deprecated.",
issue=11859,
replacement="to use --config-settings",
gone_in="25.0",
gone_in=None,
)
logger.warning(
"Implying --no-binary=:all: due to the presence of "

View File

@ -309,7 +309,7 @@ class Factory:
specifier=specifier,
hashes=hashes,
)
icans = list(result.iter_applicable())
icans = result.applicable_candidates
# PEP 592: Yanked releases are ignored unless the specifier
# explicitly pins a version (via '==' or '===') that can be

View File

@ -26,7 +26,11 @@ from pip._internal.utils.entrypoints import (
get_best_invocation_for_this_python,
)
from pip._internal.utils.filesystem import adjacent_tmp_file, check_path_owner, replace
from pip._internal.utils.misc import ensure_dir
from pip._internal.utils.misc import (
ExternallyManagedEnvironment,
check_externally_managed,
ensure_dir,
)
_WEEK = datetime.timedelta(days=7)
@ -231,6 +235,10 @@ def pip_self_version_check(session: PipSession, options: optparse.Values) -> Non
installed_dist = get_default_environment().get_distribution("pip")
if not installed_dist:
return
try:
check_externally_managed()
except ExternallyManagedEnvironment:
return
upgrade_prompt = _self_version_check_logic(
state=SelfCheckState(cache_dir=options.cache_dir),

View File

@ -1,36 +0,0 @@
import codecs
import locale
import re
import sys
from typing import List, Tuple
BOMS: List[Tuple[bytes, str]] = [
(codecs.BOM_UTF8, "utf-8"),
(codecs.BOM_UTF16, "utf-16"),
(codecs.BOM_UTF16_BE, "utf-16-be"),
(codecs.BOM_UTF16_LE, "utf-16-le"),
(codecs.BOM_UTF32, "utf-32"),
(codecs.BOM_UTF32_BE, "utf-32-be"),
(codecs.BOM_UTF32_LE, "utf-32-le"),
]
ENCODING_RE = re.compile(rb"coding[:=]\s*([-\w.]+)")
def auto_decode(data: bytes) -> str:
"""Check a bytes string for a BOM to correctly detect the encoding
Fallback to locale.getpreferredencoding(False) like open() on Python3"""
for bom, encoding in BOMS:
if data.startswith(bom):
return data[len(bom) :].decode(encoding)
# Lets check the first two lines as in PEP263
for line in data.split(b"\n")[:2]:
if line[0:1] == b"#" and ENCODING_RE.search(line):
result = ENCODING_RE.search(line)
assert result is not None
encoding = result.groups()[0].decode("ascii")
return data.decode(encoding)
return data.decode(
locale.getpreferredencoding(False) or sys.getdefaultencoding(),
)

View File

@ -137,12 +137,19 @@ class IndentedRenderable:
yield Segment("\n")
class PipConsole(Console):
def on_broken_pipe(self) -> None:
# Reraise the original exception, rich 13.8.0+ exits by default
# instead, preventing our handler from firing.
raise BrokenPipeError() from None
class RichPipStreamHandler(RichHandler):
KEYWORDS: ClassVar[Optional[List[str]]] = []
def __init__(self, stream: Optional[TextIO], no_color: bool) -> None:
super().__init__(
console=Console(file=stream, no_color=no_color, soft_wrap=True),
console=PipConsole(file=stream, no_color=no_color, soft_wrap=True),
show_time=False,
show_level=False,
show_path=False,

View File

@ -19,12 +19,13 @@ from typing import (
Any,
BinaryIO,
Callable,
Dict,
Generator,
Iterable,
Iterator,
List,
Mapping,
Optional,
Sequence,
TextIO,
Tuple,
Type,
@ -667,7 +668,7 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
def build_wheel(
self,
wheel_directory: str,
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
config_settings: Optional[Mapping[str, Any]] = None,
metadata_directory: Optional[str] = None,
) -> str:
cs = self.config_holder.config_settings
@ -678,7 +679,7 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
def build_sdist(
self,
sdist_directory: str,
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
config_settings: Optional[Mapping[str, Any]] = None,
) -> str:
cs = self.config_holder.config_settings
return super().build_sdist(sdist_directory, config_settings=cs)
@ -686,7 +687,7 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
def build_editable(
self,
wheel_directory: str,
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
config_settings: Optional[Mapping[str, Any]] = None,
metadata_directory: Optional[str] = None,
) -> str:
cs = self.config_holder.config_settings
@ -695,27 +696,27 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
)
def get_requires_for_build_wheel(
self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None
) -> List[str]:
self, config_settings: Optional[Mapping[str, Any]] = None
) -> Sequence[str]:
cs = self.config_holder.config_settings
return super().get_requires_for_build_wheel(config_settings=cs)
def get_requires_for_build_sdist(
self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None
) -> List[str]:
self, config_settings: Optional[Mapping[str, Any]] = None
) -> Sequence[str]:
cs = self.config_holder.config_settings
return super().get_requires_for_build_sdist(config_settings=cs)
def get_requires_for_build_editable(
self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None
) -> List[str]:
self, config_settings: Optional[Mapping[str, Any]] = None
) -> Sequence[str]:
cs = self.config_holder.config_settings
return super().get_requires_for_build_editable(config_settings=cs)
def prepare_metadata_for_build_wheel(
self,
metadata_directory: str,
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
config_settings: Optional[Mapping[str, Any]] = None,
_allow_fallback: bool = True,
) -> str:
cs = self.config_holder.config_settings
@ -728,9 +729,9 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
def prepare_metadata_for_build_editable(
self,
metadata_directory: str,
config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
config_settings: Optional[Mapping[str, Any]] = None,
_allow_fallback: bool = True,
) -> str:
) -> Optional[str]:
cs = self.config_holder.config_settings
return super().prepare_metadata_for_build_editable(
metadata_directory=metadata_directory,
@ -764,7 +765,7 @@ def warn_if_run_as_root() -> None:
logger.warning(
"Running pip as the 'root' user can result in broken permissions and "
"conflicting behaviour with the system package manager, possibly "
"rendering your system unusable."
"rendering your system unusable. "
"It is recommended to use a virtual environment instead: "
"https://pip.pypa.io/warnings/venv. "
"Use the --root-user-action option if you know what you are doing and "

View File

@ -11,6 +11,7 @@ NormalizedExtra = NewType("NormalizedExtra", str)
logger = logging.getLogger(__name__)
@functools.lru_cache(maxsize=32)
def check_requires_python(
requires_python: Optional[str], version_info: Tuple[int, ...]
) -> bool:

View File

@ -176,7 +176,7 @@ def untar_file(filename: str, location: str) -> None:
)
mode = "r:*"
tar = tarfile.open(filename, mode, encoding="utf-8")
tar = tarfile.open(filename, mode, encoding="utf-8") # type: ignore
try:
leading = has_leading_dir([member.name for member in tar.getmembers()])

View File

@ -6,9 +6,10 @@
Make it easy to import from cachecontrol without long namespaces.
"""
__author__ = "Eric Larson"
__email__ = "eric@ionrock.org"
__version__ = "0.14.0"
__version__ = "0.14.1"
from pip._vendor.cachecontrol.adapter import CacheControlAdapter
from pip._vendor.cachecontrol.controller import CacheController

View File

@ -77,7 +77,7 @@ class CacheControlAdapter(HTTPAdapter):
return resp
def build_response(
def build_response( # type: ignore[override]
self,
request: PreparedRequest,
response: HTTPResponse,
@ -143,7 +143,7 @@ class CacheControlAdapter(HTTPAdapter):
_update_chunk_length, response
)
resp: Response = super().build_response(request, response) # type: ignore[no-untyped-call]
resp: Response = super().build_response(request, response)
# See if we should invalidate the cache.
if request.method in self.invalidating_methods and resp.ok:

View File

@ -6,6 +6,7 @@
The cache object API for implementing caches. The default is a thread
safe in-memory dictionary.
"""
from __future__ import annotations
from threading import Lock

View File

@ -6,7 +6,7 @@ from __future__ import annotations
import hashlib
import os
from textwrap import dedent
from typing import IO, TYPE_CHECKING, Union
from typing import IO, TYPE_CHECKING
from pathlib import Path
from pip._vendor.cachecontrol.cache import BaseCache, SeparateBodyBaseCache

View File

@ -5,6 +5,7 @@
"""
The httplib2 algorithms ported for use with requests.
"""
from __future__ import annotations
import calendar

View File

@ -38,10 +38,10 @@ class CallbackFileWrapper:
self.__callback = callback
def __getattr__(self, name: str) -> Any:
# The vaguaries of garbage collection means that self.__fp is
# The vagaries of garbage collection means that self.__fp is
# not always set. By using __getattribute__ and the private
# name[0] allows looking up the attribute value and raising an
# AttributeError when it doesn't exist. This stop thigns from
# AttributeError when it doesn't exist. This stop things from
# infinitely recursing calls to getattr in the case where
# self.__fp hasn't been set.
#

View File

@ -68,7 +68,10 @@ class OneDayCache(BaseHeuristic):
if "expires" not in response.headers:
date = parsedate(response.headers["date"])
expires = expire_after(timedelta(days=1), date=datetime(*date[:6], tzinfo=timezone.utc)) # type: ignore[index,misc]
expires = expire_after(
timedelta(days=1),
date=datetime(*date[:6], tzinfo=timezone.utc), # type: ignore[index,misc]
)
headers["expires"] = datetime_to_header(expires)
headers["cache-control"] = "public"
return headers

View File

@ -1,4 +1,3 @@
from .package_data import __version__
from .core import (
IDNABidiError,
IDNAError,
@ -20,8 +19,10 @@ from .core import (
valid_string_length,
)
from .intranges import intranges_contain
from .package_data import __version__
__all__ = [
"__version__",
"IDNABidiError",
"IDNAError",
"InvalidCodepoint",

View File

@ -1,49 +1,51 @@
from .core import encode, decode, alabel, ulabel, IDNAError
import codecs
import re
from typing import Any, Tuple, Optional
from typing import Any, Optional, Tuple
from .core import IDNAError, alabel, decode, encode, ulabel
_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]")
_unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]')
class Codec(codecs.Codec):
def encode(self, data: str, errors: str = 'strict') -> Tuple[bytes, int]:
if errors != 'strict':
raise IDNAError('Unsupported error handling \"{}\"'.format(errors))
def encode(self, data: str, errors: str = "strict") -> Tuple[bytes, int]:
if errors != "strict":
raise IDNAError('Unsupported error handling "{}"'.format(errors))
if not data:
return b"", 0
return encode(data), len(data)
def decode(self, data: bytes, errors: str = 'strict') -> Tuple[str, int]:
if errors != 'strict':
raise IDNAError('Unsupported error handling \"{}\"'.format(errors))
def decode(self, data: bytes, errors: str = "strict") -> Tuple[str, int]:
if errors != "strict":
raise IDNAError('Unsupported error handling "{}"'.format(errors))
if not data:
return '', 0
return "", 0
return decode(data), len(data)
class IncrementalEncoder(codecs.BufferedIncrementalEncoder):
def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[bytes, int]:
if errors != 'strict':
raise IDNAError('Unsupported error handling \"{}\"'.format(errors))
if errors != "strict":
raise IDNAError('Unsupported error handling "{}"'.format(errors))
if not data:
return b'', 0
return b"", 0
labels = _unicode_dots_re.split(data)
trailing_dot = b''
trailing_dot = b""
if labels:
if not labels[-1]:
trailing_dot = b'.'
trailing_dot = b"."
del labels[-1]
elif not final:
# Keep potentially unfinished label until the next call
del labels[-1]
if labels:
trailing_dot = b'.'
trailing_dot = b"."
result = []
size = 0
@ -54,32 +56,33 @@ class IncrementalEncoder(codecs.BufferedIncrementalEncoder):
size += len(label)
# Join with U+002E
result_bytes = b'.'.join(result) + trailing_dot
result_bytes = b".".join(result) + trailing_dot
size += len(trailing_dot)
return result_bytes, size
class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
def _buffer_decode(self, data: Any, errors: str, final: bool) -> Tuple[str, int]:
if errors != 'strict':
raise IDNAError('Unsupported error handling \"{}\"'.format(errors))
if errors != "strict":
raise IDNAError('Unsupported error handling "{}"'.format(errors))
if not data:
return ('', 0)
return ("", 0)
if not isinstance(data, str):
data = str(data, 'ascii')
data = str(data, "ascii")
labels = _unicode_dots_re.split(data)
trailing_dot = ''
trailing_dot = ""
if labels:
if not labels[-1]:
trailing_dot = '.'
trailing_dot = "."
del labels[-1]
elif not final:
# Keep potentially unfinished label until the next call
del labels[-1]
if labels:
trailing_dot = '.'
trailing_dot = "."
result = []
size = 0
@ -89,7 +92,7 @@ class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
size += 1
size += len(label)
result_str = '.'.join(result) + trailing_dot
result_str = ".".join(result) + trailing_dot
size += len(trailing_dot)
return (result_str, size)
@ -103,7 +106,7 @@ class StreamReader(Codec, codecs.StreamReader):
def search_function(name: str) -> Optional[codecs.CodecInfo]:
if name != 'idna2008':
if name != "idna2008":
return None
return codecs.CodecInfo(
name=name,
@ -115,4 +118,5 @@ def search_function(name: str) -> Optional[codecs.CodecInfo]:
streamreader=StreamReader,
)
codecs.register(search_function)

View File

@ -1,13 +1,15 @@
from .core import *
from .codec import *
from typing import Any, Union
from .core import decode, encode
def ToASCII(label: str) -> bytes:
return encode(label)
def ToUnicode(label: Union[bytes, bytearray]) -> str:
return decode(label)
def nameprep(s: Any) -> None:
raise NotImplementedError('IDNA 2008 does not utilise nameprep protocol')
def nameprep(s: Any) -> None:
raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol")

View File

@ -1,31 +1,37 @@
from . import idnadata
import bisect
import unicodedata
import re
from typing import Union, Optional
import unicodedata
from typing import Optional, Union
from . import idnadata
from .intranges import intranges_contain
_virama_combining_class = 9
_alabel_prefix = b'xn--'
_unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]')
_alabel_prefix = b"xn--"
_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]")
class IDNAError(UnicodeError):
""" Base exception for all IDNA-encoding related problems """
"""Base exception for all IDNA-encoding related problems"""
pass
class IDNABidiError(IDNAError):
""" Exception when bidirectional requirements are not satisfied """
"""Exception when bidirectional requirements are not satisfied"""
pass
class InvalidCodepoint(IDNAError):
""" Exception when a disallowed or unallocated codepoint is used """
"""Exception when a disallowed or unallocated codepoint is used"""
pass
class InvalidCodepointContext(IDNAError):
""" Exception when the codepoint is not valid in the context it is used """
"""Exception when the codepoint is not valid in the context it is used"""
pass
@ -33,17 +39,20 @@ def _combining_class(cp: int) -> int:
v = unicodedata.combining(chr(cp))
if v == 0:
if not unicodedata.name(chr(cp)):
raise ValueError('Unknown character in unicodedata')
raise ValueError("Unknown character in unicodedata")
return v
def _is_script(cp: str, script: str) -> bool:
return intranges_contain(ord(cp), idnadata.scripts[script])
def _punycode(s: str) -> bytes:
return s.encode('punycode')
return s.encode("punycode")
def _unot(s: int) -> str:
return 'U+{:04X}'.format(s)
return "U+{:04X}".format(s)
def valid_label_length(label: Union[bytes, str]) -> bool:
@ -61,96 +70,106 @@ def valid_string_length(label: Union[bytes, str], trailing_dot: bool) -> bool:
def check_bidi(label: str, check_ltr: bool = False) -> bool:
# Bidi rules should only be applied if string contains RTL characters
bidi_label = False
for (idx, cp) in enumerate(label, 1):
for idx, cp in enumerate(label, 1):
direction = unicodedata.bidirectional(cp)
if direction == '':
if direction == "":
# String likely comes from a newer version of Unicode
raise IDNABidiError('Unknown directionality in label {} at position {}'.format(repr(label), idx))
if direction in ['R', 'AL', 'AN']:
raise IDNABidiError("Unknown directionality in label {} at position {}".format(repr(label), idx))
if direction in ["R", "AL", "AN"]:
bidi_label = True
if not bidi_label and not check_ltr:
return True
# Bidi rule 1
direction = unicodedata.bidirectional(label[0])
if direction in ['R', 'AL']:
if direction in ["R", "AL"]:
rtl = True
elif direction == 'L':
elif direction == "L":
rtl = False
else:
raise IDNABidiError('First codepoint in label {} must be directionality L, R or AL'.format(repr(label)))
raise IDNABidiError("First codepoint in label {} must be directionality L, R or AL".format(repr(label)))
valid_ending = False
number_type = None # type: Optional[str]
for (idx, cp) in enumerate(label, 1):
number_type: Optional[str] = None
for idx, cp in enumerate(label, 1):
direction = unicodedata.bidirectional(cp)
if rtl:
# Bidi rule 2
if not direction in ['R', 'AL', 'AN', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']:
raise IDNABidiError('Invalid direction for codepoint at position {} in a right-to-left label'.format(idx))
if direction not in [
"R",
"AL",
"AN",
"EN",
"ES",
"CS",
"ET",
"ON",
"BN",
"NSM",
]:
raise IDNABidiError("Invalid direction for codepoint at position {} in a right-to-left label".format(idx))
# Bidi rule 3
if direction in ['R', 'AL', 'EN', 'AN']:
if direction in ["R", "AL", "EN", "AN"]:
valid_ending = True
elif direction != 'NSM':
elif direction != "NSM":
valid_ending = False
# Bidi rule 4
if direction in ['AN', 'EN']:
if direction in ["AN", "EN"]:
if not number_type:
number_type = direction
else:
if number_type != direction:
raise IDNABidiError('Can not mix numeral types in a right-to-left label')
raise IDNABidiError("Can not mix numeral types in a right-to-left label")
else:
# Bidi rule 5
if not direction in ['L', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']:
raise IDNABidiError('Invalid direction for codepoint at position {} in a left-to-right label'.format(idx))
if direction not in ["L", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"]:
raise IDNABidiError("Invalid direction for codepoint at position {} in a left-to-right label".format(idx))
# Bidi rule 6
if direction in ['L', 'EN']:
if direction in ["L", "EN"]:
valid_ending = True
elif direction != 'NSM':
elif direction != "NSM":
valid_ending = False
if not valid_ending:
raise IDNABidiError('Label ends with illegal codepoint directionality')
raise IDNABidiError("Label ends with illegal codepoint directionality")
return True
def check_initial_combiner(label: str) -> bool:
if unicodedata.category(label[0])[0] == 'M':
raise IDNAError('Label begins with an illegal combining character')
if unicodedata.category(label[0])[0] == "M":
raise IDNAError("Label begins with an illegal combining character")
return True
def check_hyphen_ok(label: str) -> bool:
if label[2:4] == '--':
raise IDNAError('Label has disallowed hyphens in 3rd and 4th position')
if label[0] == '-' or label[-1] == '-':
raise IDNAError('Label must not start or end with a hyphen')
if label[2:4] == "--":
raise IDNAError("Label has disallowed hyphens in 3rd and 4th position")
if label[0] == "-" or label[-1] == "-":
raise IDNAError("Label must not start or end with a hyphen")
return True
def check_nfc(label: str) -> None:
if unicodedata.normalize('NFC', label) != label:
raise IDNAError('Label must be in Normalization Form C')
if unicodedata.normalize("NFC", label) != label:
raise IDNAError("Label must be in Normalization Form C")
def valid_contextj(label: str, pos: int) -> bool:
cp_value = ord(label[pos])
if cp_value == 0x200c:
if cp_value == 0x200C:
if pos > 0:
if _combining_class(ord(label[pos - 1])) == _virama_combining_class:
return True
ok = False
for i in range(pos-1, -1, -1):
for i in range(pos - 1, -1, -1):
joining_type = idnadata.joining_types.get(ord(label[i]))
if joining_type == ord('T'):
if joining_type == ord("T"):
continue
elif joining_type in [ord('L'), ord('D')]:
elif joining_type in [ord("L"), ord("D")]:
ok = True
break
else:
@ -160,63 +179,61 @@ def valid_contextj(label: str, pos: int) -> bool:
return False
ok = False
for i in range(pos+1, len(label)):
for i in range(pos + 1, len(label)):
joining_type = idnadata.joining_types.get(ord(label[i]))
if joining_type == ord('T'):
if joining_type == ord("T"):
continue
elif joining_type in [ord('R'), ord('D')]:
elif joining_type in [ord("R"), ord("D")]:
ok = True
break
else:
break
return ok
if cp_value == 0x200d:
if cp_value == 0x200D:
if pos > 0:
if _combining_class(ord(label[pos - 1])) == _virama_combining_class:
return True
return False
else:
return False
def valid_contexto(label: str, pos: int, exception: bool = False) -> bool:
cp_value = ord(label[pos])
if cp_value == 0x00b7:
if 0 < pos < len(label)-1:
if ord(label[pos - 1]) == 0x006c and ord(label[pos + 1]) == 0x006c:
if cp_value == 0x00B7:
if 0 < pos < len(label) - 1:
if ord(label[pos - 1]) == 0x006C and ord(label[pos + 1]) == 0x006C:
return True
return False
elif cp_value == 0x0375:
if pos < len(label)-1 and len(label) > 1:
return _is_script(label[pos + 1], 'Greek')
if pos < len(label) - 1 and len(label) > 1:
return _is_script(label[pos + 1], "Greek")
return False
elif cp_value == 0x05f3 or cp_value == 0x05f4:
elif cp_value == 0x05F3 or cp_value == 0x05F4:
if pos > 0:
return _is_script(label[pos - 1], 'Hebrew')
return _is_script(label[pos - 1], "Hebrew")
return False
elif cp_value == 0x30fb:
elif cp_value == 0x30FB:
for cp in label:
if cp == '\u30fb':
if cp == "\u30fb":
continue
if _is_script(cp, 'Hiragana') or _is_script(cp, 'Katakana') or _is_script(cp, 'Han'):
if _is_script(cp, "Hiragana") or _is_script(cp, "Katakana") or _is_script(cp, "Han"):
return True
return False
elif 0x660 <= cp_value <= 0x669:
for cp in label:
if 0x6f0 <= ord(cp) <= 0x06f9:
if 0x6F0 <= ord(cp) <= 0x06F9:
return False
return True
elif 0x6f0 <= cp_value <= 0x6f9:
elif 0x6F0 <= cp_value <= 0x6F9:
for cp in label:
if 0x660 <= ord(cp) <= 0x0669:
return False
@ -227,37 +244,49 @@ def valid_contexto(label: str, pos: int, exception: bool = False) -> bool:
def check_label(label: Union[str, bytes, bytearray]) -> None:
if isinstance(label, (bytes, bytearray)):
label = label.decode('utf-8')
label = label.decode("utf-8")
if len(label) == 0:
raise IDNAError('Empty Label')
raise IDNAError("Empty Label")
check_nfc(label)
check_hyphen_ok(label)
check_initial_combiner(label)
for (pos, cp) in enumerate(label):
for pos, cp in enumerate(label):
cp_value = ord(cp)
if intranges_contain(cp_value, idnadata.codepoint_classes['PVALID']):
if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]):
continue
elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTJ']):
if not valid_contextj(label, pos):
raise InvalidCodepointContext('Joiner {} not allowed at position {} in {}'.format(
_unot(cp_value), pos+1, repr(label)))
elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTO']):
elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]):
try:
if not valid_contextj(label, pos):
raise InvalidCodepointContext(
"Joiner {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label))
)
except ValueError:
raise IDNAError(
"Unknown codepoint adjacent to joiner {} at position {} in {}".format(
_unot(cp_value), pos + 1, repr(label)
)
)
elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTO"]):
if not valid_contexto(label, pos):
raise InvalidCodepointContext('Codepoint {} not allowed at position {} in {}'.format(_unot(cp_value), pos+1, repr(label)))
raise InvalidCodepointContext(
"Codepoint {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label))
)
else:
raise InvalidCodepoint('Codepoint {} at position {} of {} not allowed'.format(_unot(cp_value), pos+1, repr(label)))
raise InvalidCodepoint(
"Codepoint {} at position {} of {} not allowed".format(_unot(cp_value), pos + 1, repr(label))
)
check_bidi(label)
def alabel(label: str) -> bytes:
try:
label_bytes = label.encode('ascii')
label_bytes = label.encode("ascii")
ulabel(label_bytes)
if not valid_label_length(label_bytes):
raise IDNAError('Label too long')
raise IDNAError("Label too long")
return label_bytes
except UnicodeEncodeError:
pass
@ -266,7 +295,7 @@ def alabel(label: str) -> bytes:
label_bytes = _alabel_prefix + _punycode(label)
if not valid_label_length(label_bytes):
raise IDNAError('Label too long')
raise IDNAError("Label too long")
return label_bytes
@ -274,7 +303,7 @@ def alabel(label: str) -> bytes:
def ulabel(label: Union[str, bytes, bytearray]) -> str:
if not isinstance(label, (bytes, bytearray)):
try:
label_bytes = label.encode('ascii')
label_bytes = label.encode("ascii")
except UnicodeEncodeError:
check_label(label)
return label
@ -283,19 +312,19 @@ def ulabel(label: Union[str, bytes, bytearray]) -> str:
label_bytes = label_bytes.lower()
if label_bytes.startswith(_alabel_prefix):
label_bytes = label_bytes[len(_alabel_prefix):]
label_bytes = label_bytes[len(_alabel_prefix) :]
if not label_bytes:
raise IDNAError('Malformed A-label, no Punycode eligible content found')
if label_bytes.decode('ascii')[-1] == '-':
raise IDNAError('A-label must not end with a hyphen')
raise IDNAError("Malformed A-label, no Punycode eligible content found")
if label_bytes.decode("ascii")[-1] == "-":
raise IDNAError("A-label must not end with a hyphen")
else:
check_label(label_bytes)
return label_bytes.decode('ascii')
return label_bytes.decode("ascii")
try:
label = label_bytes.decode('punycode')
label = label_bytes.decode("punycode")
except UnicodeError:
raise IDNAError('Invalid A-label')
raise IDNAError("Invalid A-label")
check_label(label)
return label
@ -303,52 +332,60 @@ def ulabel(label: Union[str, bytes, bytearray]) -> str:
def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str:
"""Re-map the characters in the string according to UTS46 processing."""
from .uts46data import uts46data
output = ''
output = ""
for pos, char in enumerate(domain):
code_point = ord(char)
try:
uts46row = uts46data[code_point if code_point < 256 else
bisect.bisect_left(uts46data, (code_point, 'Z')) - 1]
uts46row = uts46data[code_point if code_point < 256 else bisect.bisect_left(uts46data, (code_point, "Z")) - 1]
status = uts46row[1]
replacement = None # type: Optional[str]
replacement: Optional[str] = None
if len(uts46row) == 3:
replacement = uts46row[2]
if (status == 'V' or
(status == 'D' and not transitional) or
(status == '3' and not std3_rules and replacement is None)):
if (
status == "V"
or (status == "D" and not transitional)
or (status == "3" and not std3_rules and replacement is None)
):
output += char
elif replacement is not None and (status == 'M' or
(status == '3' and not std3_rules) or
(status == 'D' and transitional)):
elif replacement is not None and (
status == "M" or (status == "3" and not std3_rules) or (status == "D" and transitional)
):
output += replacement
elif status != 'I':
elif status != "I":
raise IndexError()
except IndexError:
raise InvalidCodepoint(
'Codepoint {} not allowed at position {} in {}'.format(
_unot(code_point), pos + 1, repr(domain)))
"Codepoint {} not allowed at position {} in {}".format(_unot(code_point), pos + 1, repr(domain))
)
return unicodedata.normalize('NFC', output)
return unicodedata.normalize("NFC", output)
def encode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool = False, std3_rules: bool = False, transitional: bool = False) -> bytes:
def encode(
s: Union[str, bytes, bytearray],
strict: bool = False,
uts46: bool = False,
std3_rules: bool = False,
transitional: bool = False,
) -> bytes:
if not isinstance(s, str):
try:
s = str(s, 'ascii')
s = str(s, "ascii")
except UnicodeDecodeError:
raise IDNAError('should pass a unicode string to the function rather than a byte string.')
raise IDNAError("should pass a unicode string to the function rather than a byte string.")
if uts46:
s = uts46_remap(s, std3_rules, transitional)
trailing_dot = False
result = []
if strict:
labels = s.split('.')
labels = s.split(".")
else:
labels = _unicode_dots_re.split(s)
if not labels or labels == ['']:
raise IDNAError('Empty domain')
if labels[-1] == '':
if not labels or labels == [""]:
raise IDNAError("Empty domain")
if labels[-1] == "":
del labels[-1]
trailing_dot = True
for label in labels:
@ -356,21 +393,26 @@ def encode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool =
if s:
result.append(s)
else:
raise IDNAError('Empty label')
raise IDNAError("Empty label")
if trailing_dot:
result.append(b'')
s = b'.'.join(result)
result.append(b"")
s = b".".join(result)
if not valid_string_length(s, trailing_dot):
raise IDNAError('Domain too long')
raise IDNAError("Domain too long")
return s
def decode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool = False, std3_rules: bool = False) -> str:
def decode(
s: Union[str, bytes, bytearray],
strict: bool = False,
uts46: bool = False,
std3_rules: bool = False,
) -> str:
try:
if not isinstance(s, str):
s = str(s, 'ascii')
s = str(s, "ascii")
except UnicodeDecodeError:
raise IDNAError('Invalid ASCII in A-label')
raise IDNAError("Invalid ASCII in A-label")
if uts46:
s = uts46_remap(s, std3_rules, False)
trailing_dot = False
@ -378,9 +420,9 @@ def decode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool =
if not strict:
labels = _unicode_dots_re.split(s)
else:
labels = s.split('.')
if not labels or labels == ['']:
raise IDNAError('Empty domain')
labels = s.split(".")
if not labels or labels == [""]:
raise IDNAError("Empty domain")
if not labels[-1]:
del labels[-1]
trailing_dot = True
@ -389,7 +431,7 @@ def decode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool =
if s:
result.append(s)
else:
raise IDNAError('Empty label')
raise IDNAError("Empty label")
if trailing_dot:
result.append('')
return '.'.join(result)
result.append("")
return ".".join(result)

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@ in the original list?" in time O(log(# runs)).
import bisect
from typing import List, Tuple
def intranges_from_list(list_: List[int]) -> Tuple[int, ...]:
"""Represent a list of integers as a sequence of ranges:
((start_0, end_0), (start_1, end_1), ...), such that the original
@ -20,18 +21,20 @@ def intranges_from_list(list_: List[int]) -> Tuple[int, ...]:
ranges = []
last_write = -1
for i in range(len(sorted_list)):
if i+1 < len(sorted_list):
if sorted_list[i] == sorted_list[i+1]-1:
if i + 1 < len(sorted_list):
if sorted_list[i] == sorted_list[i + 1] - 1:
continue
current_range = sorted_list[last_write+1:i+1]
current_range = sorted_list[last_write + 1 : i + 1]
ranges.append(_encode_range(current_range[0], current_range[-1] + 1))
last_write = i
return tuple(ranges)
def _encode_range(start: int, end: int) -> int:
return (start << 32) | end
def _decode_range(r: int) -> Tuple[int, int]:
return (r >> 32), (r & ((1 << 32) - 1))
@ -43,7 +46,7 @@ def intranges_contain(int_: int, ranges: Tuple[int, ...]) -> bool:
# we could be immediately ahead of a tuple (start, end)
# with start < int_ <= end
if pos > 0:
left, right = _decode_range(ranges[pos-1])
left, right = _decode_range(ranges[pos - 1])
if left <= int_ < right:
return True
# or we could be immediately behind a tuple (int_, end)

View File

@ -1,2 +1 @@
__version__ = '3.7'
__version__ = "3.10"

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,20 @@
from .exceptions import *
from .ext import ExtType, Timestamp
# ruff: noqa: F401
import os
from .exceptions import * # noqa: F403
from .ext import ExtType, Timestamp
version = (1, 0, 8)
__version__ = "1.0.8"
version = (1, 1, 0)
__version__ = "1.1.0"
if os.environ.get("MSGPACK_PUREPYTHON"):
from .fallback import Packer, unpackb, Unpacker
from .fallback import Packer, Unpacker, unpackb
else:
try:
from ._cmsgpack import Packer, unpackb, Unpacker
from ._cmsgpack import Packer, Unpacker, unpackb
except ImportError:
from .fallback import Packer, unpackb, Unpacker
from .fallback import Packer, Unpacker, unpackb
def pack(o, stream, **kwargs):

View File

@ -1,6 +1,6 @@
from collections import namedtuple
import datetime
import struct
from collections import namedtuple
class ExtType(namedtuple("ExtType", "code data")):
@ -157,7 +157,9 @@ class Timestamp:
:rtype: `datetime.datetime`
"""
utc = datetime.timezone.utc
return datetime.datetime.fromtimestamp(0, utc) + datetime.timedelta(seconds=self.to_unix())
return datetime.datetime.fromtimestamp(0, utc) + datetime.timedelta(
seconds=self.seconds, microseconds=self.nanoseconds // 1000
)
@staticmethod
def from_datetime(dt):
@ -165,4 +167,4 @@ class Timestamp:
:rtype: Timestamp
"""
return Timestamp.from_unix(dt.timestamp())
return Timestamp(seconds=int(dt.timestamp()), nanoseconds=dt.microsecond * 1000)

View File

@ -1,27 +1,22 @@
"""Fallback pure Python implementation of msgpack"""
from datetime import datetime as _DateTime
import sys
import struct
import struct
import sys
from datetime import datetime as _DateTime
if hasattr(sys, "pypy_version_info"):
# StringIO is slow on PyPy, StringIO is faster. However: PyPy's own
# StringBuilder is fastest.
from __pypy__ import newlist_hint
from __pypy__.builders import BytesBuilder
try:
from __pypy__.builders import BytesBuilder as StringBuilder
except ImportError:
from __pypy__.builders import StringBuilder
USING_STRINGBUILDER = True
_USING_STRINGBUILDER = True
class StringIO:
class BytesIO:
def __init__(self, s=b""):
if s:
self.builder = StringBuilder(len(s))
self.builder = BytesBuilder(len(s))
self.builder.append(s)
else:
self.builder = StringBuilder()
self.builder = BytesBuilder()
def write(self, s):
if isinstance(s, memoryview):
@ -34,17 +29,17 @@ if hasattr(sys, "pypy_version_info"):
return self.builder.build()
else:
USING_STRINGBUILDER = False
from io import BytesIO as StringIO
from io import BytesIO
newlist_hint = lambda size: []
_USING_STRINGBUILDER = False
def newlist_hint(size):
return []
from .exceptions import BufferFull, OutOfData, ExtraData, FormatError, StackError
from .exceptions import BufferFull, ExtraData, FormatError, OutOfData, StackError
from .ext import ExtType, Timestamp
EX_SKIP = 0
EX_CONSTRUCT = 1
EX_READ_ARRAY_HEADER = 2
@ -231,6 +226,7 @@ class Unpacker:
def __init__(
self,
file_like=None,
*,
read_size=0,
use_list=True,
raw=False,
@ -333,6 +329,7 @@ class Unpacker:
# Use extend here: INPLACE_ADD += doesn't reliably typecast memoryview in jython
self._buffer.extend(view)
view.release()
def _consume(self):
"""Gets rid of the used parts of the buffer."""
@ -649,32 +646,13 @@ class Packer:
The error handler for encoding unicode. (default: 'strict')
DO NOT USE THIS!! This option is kept for very specific usage.
Example of streaming deserialize from file-like object::
unpacker = Unpacker(file_like)
for o in unpacker:
process(o)
Example of streaming deserialize from socket::
unpacker = Unpacker()
while True:
buf = sock.recv(1024**2)
if not buf:
break
unpacker.feed(buf)
for o in unpacker:
process(o)
Raises ``ExtraData`` when *packed* contains extra bytes.
Raises ``OutOfData`` when *packed* is incomplete.
Raises ``FormatError`` when *packed* is not valid msgpack.
Raises ``StackError`` when *packed* contains too nested.
Other exceptions can be raised during unpacking.
:param int buf_size:
Internal buffer size. This option is used only for C implementation.
"""
def __init__(
self,
*,
default=None,
use_single_float=False,
autoreset=True,
@ -682,17 +660,17 @@ class Packer:
strict_types=False,
datetime=False,
unicode_errors=None,
buf_size=None,
):
self._strict_types = strict_types
self._use_float = use_single_float
self._autoreset = autoreset
self._use_bin_type = use_bin_type
self._buffer = StringIO()
self._buffer = BytesIO()
self._datetime = bool(datetime)
self._unicode_errors = unicode_errors or "strict"
if default is not None:
if not callable(default):
raise TypeError("default must be callable")
if default is not None and not callable(default):
raise TypeError("default must be callable")
self._default = default
def _pack(
@ -823,18 +801,18 @@ class Packer:
try:
self._pack(obj)
except:
self._buffer = StringIO() # force reset
self._buffer = BytesIO() # force reset
raise
if self._autoreset:
ret = self._buffer.getvalue()
self._buffer = StringIO()
self._buffer = BytesIO()
return ret
def pack_map_pairs(self, pairs):
self._pack_map_pairs(len(pairs), pairs)
if self._autoreset:
ret = self._buffer.getvalue()
self._buffer = StringIO()
self._buffer = BytesIO()
return ret
def pack_array_header(self, n):
@ -843,7 +821,7 @@ class Packer:
self._pack_array_header(n)
if self._autoreset:
ret = self._buffer.getvalue()
self._buffer = StringIO()
self._buffer = BytesIO()
return ret
def pack_map_header(self, n):
@ -852,7 +830,7 @@ class Packer:
self._pack_map_header(n)
if self._autoreset:
ret = self._buffer.getvalue()
self._buffer = StringIO()
self._buffer = BytesIO()
return ret
def pack_ext_type(self, typecode, data):
@ -941,11 +919,11 @@ class Packer:
This method is useful only when autoreset=False.
"""
self._buffer = StringIO()
self._buffer = BytesIO()
def getbuffer(self):
"""Return view of internal buffer."""
if USING_STRINGBUILDER:
if _USING_STRINGBUILDER:
return memoryview(self.bytes())
else:
return self._buffer.getbuffer()

View File

@ -6,10 +6,10 @@ __title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"
__version__ = "24.1"
__version__ = "24.2"
__author__ = "Donald Stufft and individual contributors"
__email__ = "donald@stufft.io"
__license__ = "BSD-2-Clause or Apache-2.0"
__copyright__ = "2014 %s" % __author__
__copyright__ = f"2014 {__author__}"

View File

@ -48,8 +48,8 @@ class ELFFile:
try:
ident = self._read("16B")
except struct.error:
raise ELFInvalid("unable to parse identification")
except struct.error as e:
raise ELFInvalid("unable to parse identification") from e
magic = bytes(ident[:4])
if magic != b"\x7fELF":
raise ELFInvalid(f"invalid magic: {magic!r}")
@ -67,11 +67,11 @@ class ELFFile:
(2, 1): ("<HHIQQQIHHH", "<IIQQQQQQ", (0, 2, 5)), # 64-bit LSB.
(2, 2): (">HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB.
}[(self.capacity, self.encoding)]
except KeyError:
except KeyError as e:
raise ELFInvalid(
f"unrecognized capacity ({self.capacity}) or "
f"encoding ({self.encoding})"
)
) from e
try:
(

View File

@ -164,6 +164,7 @@ def _parse_glibc_version(version_str: str) -> tuple[int, int]:
f"Expected glibc version with 2 components major.minor,"
f" got: {version_str}",
RuntimeWarning,
stacklevel=2,
)
return -1, -1
return int(m.group("major")), int(m.group("minor"))

View File

@ -18,9 +18,9 @@ from .utils import canonicalize_name
__all__ = [
"InvalidMarker",
"Marker",
"UndefinedComparison",
"UndefinedEnvironmentName",
"Marker",
"default_environment",
]
@ -232,7 +232,7 @@ def _evaluate_markers(markers: MarkerList, environment: dict[str, str]) -> bool:
def format_full_version(info: sys._version_info) -> str:
version = "{0.major}.{0.minor}.{0.micro}".format(info)
version = f"{info.major}.{info.minor}.{info.micro}"
kind = info.releaselevel
if kind != "final":
version += kind[0] + str(info.serial)
@ -309,12 +309,6 @@ class Marker:
"""
current_environment = cast("dict[str, str]", default_environment())
current_environment["extra"] = ""
# Work around platform.python_version() returning something that is not PEP 440
# compliant for non-tagged Python builds. We preserve default_environment()'s
# behavior of returning platform.python_version() verbatim, and leave it to the
# caller to provide a syntactically valid version if they want to override it.
if current_environment["python_full_version"].endswith("+"):
current_environment["python_full_version"] += "local"
if environment is not None:
current_environment.update(environment)
# The API used to allow setting extra to None. We need to handle this
@ -322,4 +316,16 @@ class Marker:
if current_environment["extra"] is None:
current_environment["extra"] = ""
return _evaluate_markers(self._markers, current_environment)
return _evaluate_markers(
self._markers, _repair_python_full_version(current_environment)
)
def _repair_python_full_version(env: dict[str, str]) -> dict[str, str]:
"""
Work around platform.python_version() returning something that is not PEP 440
compliant for non-tagged Python builds.
"""
if env["python_full_version"].endswith("+"):
env["python_full_version"] += "local"
return env

View File

@ -5,6 +5,8 @@ import email.header
import email.message
import email.parser
import email.policy
import pathlib
import sys
import typing
from typing import (
Any,
@ -15,15 +17,16 @@ from typing import (
cast,
)
from . import requirements, specifiers, utils
from . import licenses, requirements, specifiers, utils
from . import version as version_module
from .licenses import NormalizedLicenseExpression
T = typing.TypeVar("T")
try:
ExceptionGroup
except NameError: # pragma: no cover
if sys.version_info >= (3, 11): # pragma: no cover
ExceptionGroup = ExceptionGroup
else: # pragma: no cover
class ExceptionGroup(Exception):
"""A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11.
@ -42,9 +45,6 @@ except NameError: # pragma: no cover
def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})"
else: # pragma: no cover
ExceptionGroup = ExceptionGroup
class InvalidMetadata(ValueError):
"""A metadata field contains invalid data."""
@ -128,6 +128,10 @@ class RawMetadata(TypedDict, total=False):
# No new fields were added in PEP 685, just some edge case were
# tightened up to provide better interoptability.
# Metadata 2.4 - PEP 639
license_expression: str
license_files: list[str]
_STRING_FIELDS = {
"author",
@ -137,6 +141,7 @@ _STRING_FIELDS = {
"download_url",
"home_page",
"license",
"license_expression",
"maintainer",
"maintainer_email",
"metadata_version",
@ -149,6 +154,7 @@ _STRING_FIELDS = {
_LIST_FIELDS = {
"classifiers",
"dynamic",
"license_files",
"obsoletes",
"obsoletes_dist",
"platforms",
@ -167,7 +173,7 @@ _DICT_FIELDS = {
def _parse_keywords(data: str) -> list[str]:
"""Split a string of comma-separate keyboards into a list of keywords."""
"""Split a string of comma-separated keywords into a list of keywords."""
return [k.strip() for k in data.split(",")]
@ -216,16 +222,18 @@ def _get_payload(msg: email.message.Message, source: bytes | str) -> str:
# If our source is a str, then our caller has managed encodings for us,
# and we don't need to deal with it.
if isinstance(source, str):
payload: str = msg.get_payload()
payload = msg.get_payload()
assert isinstance(payload, str)
return payload
# If our source is a bytes, then we're managing the encoding and we need
# to deal with it.
else:
bpayload: bytes = msg.get_payload(decode=True)
bpayload = msg.get_payload(decode=True)
assert isinstance(bpayload, bytes)
try:
return bpayload.decode("utf8", "strict")
except UnicodeDecodeError:
raise ValueError("payload in an invalid encoding")
except UnicodeDecodeError as exc:
raise ValueError("payload in an invalid encoding") from exc
# The various parse_FORMAT functions here are intended to be as lenient as
@ -251,6 +259,8 @@ _EMAIL_TO_RAW_MAPPING = {
"home-page": "home_page",
"keywords": "keywords",
"license": "license",
"license-expression": "license_expression",
"license-file": "license_files",
"maintainer": "maintainer",
"maintainer-email": "maintainer_email",
"metadata-version": "metadata_version",
@ -426,7 +436,7 @@ def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]:
payload = _get_payload(parsed, data)
except ValueError:
unparsed.setdefault("description", []).append(
parsed.get_payload(decode=isinstance(data, bytes))
parsed.get_payload(decode=isinstance(data, bytes)) # type: ignore[call-overload]
)
else:
if payload:
@ -453,8 +463,8 @@ _NOT_FOUND = object()
# Keep the two values in sync.
_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"]
_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"]
_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4"]
_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4"]
_REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"])
@ -535,7 +545,7 @@ class _Validator(Generic[T]):
except utils.InvalidName as exc:
raise self._invalid_metadata(
f"{value!r} is invalid for {{field}}", cause=exc
)
) from exc
else:
return value
@ -547,7 +557,7 @@ class _Validator(Generic[T]):
except version_module.InvalidVersion as exc:
raise self._invalid_metadata(
f"{value!r} is invalid for {{field}}", cause=exc
)
) from exc
def _process_summary(self, value: str) -> str:
"""Check the field contains no newlines."""
@ -591,10 +601,12 @@ class _Validator(Generic[T]):
for dynamic_field in map(str.lower, value):
if dynamic_field in {"name", "version", "metadata-version"}:
raise self._invalid_metadata(
f"{value!r} is not allowed as a dynamic field"
f"{dynamic_field!r} is not allowed as a dynamic field"
)
elif dynamic_field not in _EMAIL_TO_RAW_MAPPING:
raise self._invalid_metadata(f"{value!r} is not a valid dynamic field")
raise self._invalid_metadata(
f"{dynamic_field!r} is not a valid dynamic field"
)
return list(map(str.lower, value))
def _process_provides_extra(
@ -608,7 +620,7 @@ class _Validator(Generic[T]):
except utils.InvalidName as exc:
raise self._invalid_metadata(
f"{name!r} is invalid for {{field}}", cause=exc
)
) from exc
else:
return normalized_names
@ -618,7 +630,7 @@ class _Validator(Generic[T]):
except specifiers.InvalidSpecifier as exc:
raise self._invalid_metadata(
f"{value!r} is invalid for {{field}}", cause=exc
)
) from exc
def _process_requires_dist(
self,
@ -629,10 +641,49 @@ class _Validator(Generic[T]):
for req in value:
reqs.append(requirements.Requirement(req))
except requirements.InvalidRequirement as exc:
raise self._invalid_metadata(f"{req!r} is invalid for {{field}}", cause=exc)
raise self._invalid_metadata(
f"{req!r} is invalid for {{field}}", cause=exc
) from exc
else:
return reqs
def _process_license_expression(
self, value: str
) -> NormalizedLicenseExpression | None:
try:
return licenses.canonicalize_license_expression(value)
except ValueError as exc:
raise self._invalid_metadata(
f"{value!r} is invalid for {{field}}", cause=exc
) from exc
def _process_license_files(self, value: list[str]) -> list[str]:
paths = []
for path in value:
if ".." in path:
raise self._invalid_metadata(
f"{path!r} is invalid for {{field}}, "
"parent directory indicators are not allowed"
)
if "*" in path:
raise self._invalid_metadata(
f"{path!r} is invalid for {{field}}, paths must be resolved"
)
if (
pathlib.PurePosixPath(path).is_absolute()
or pathlib.PureWindowsPath(path).is_absolute()
):
raise self._invalid_metadata(
f"{path!r} is invalid for {{field}}, paths must be relative"
)
if pathlib.PureWindowsPath(path).as_posix() != path:
raise self._invalid_metadata(
f"{path!r} is invalid for {{field}}, "
"paths must use '/' delimiter"
)
paths.append(path)
return paths
class Metadata:
"""Representation of distribution metadata.
@ -688,8 +739,8 @@ class Metadata:
field = _RAW_TO_EMAIL_MAPPING[key]
exc = InvalidMetadata(
field,
"{field} introduced in metadata version "
"{field_metadata_version}, not {metadata_version}",
f"{field} introduced in metadata version "
f"{field_metadata_version}, not {metadata_version}",
)
exceptions.append(exc)
continue
@ -733,6 +784,8 @@ class Metadata:
metadata_version: _Validator[_MetadataVersion] = _Validator()
""":external:ref:`core-metadata-metadata-version`
(required; validated to be a valid metadata version)"""
# `name` is not normalized/typed to NormalizedName so as to provide access to
# the original/raw name.
name: _Validator[str] = _Validator()
""":external:ref:`core-metadata-name`
(required; validated using :func:`~packaging.utils.canonicalize_name` and its
@ -770,6 +823,12 @@ class Metadata:
""":external:ref:`core-metadata-maintainer-email`"""
license: _Validator[str | None] = _Validator()
""":external:ref:`core-metadata-license`"""
license_expression: _Validator[NormalizedLicenseExpression | None] = _Validator(
added="2.4"
)
""":external:ref:`core-metadata-license-expression`"""
license_files: _Validator[list[str] | None] = _Validator(added="2.4")
""":external:ref:`core-metadata-license-file`"""
classifiers: _Validator[list[str] | None] = _Validator(added="1.1")
""":external:ref:`core-metadata-classifier`"""
requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator(

View File

@ -234,7 +234,7 @@ class Specifier(BaseSpecifier):
"""
match = self._regex.search(spec)
if not match:
raise InvalidSpecifier(f"Invalid specifier: '{spec}'")
raise InvalidSpecifier(f"Invalid specifier: {spec!r}")
self._spec: tuple[str, str] = (
match.group("operator").strip(),
@ -256,7 +256,7 @@ class Specifier(BaseSpecifier):
# operators, and if they are if they are including an explicit
# prerelease.
operator, version = self._spec
if operator in ["==", ">=", "<=", "~=", "==="]:
if operator in ["==", ">=", "<=", "~=", "===", ">", "<"]:
# The == specifier can include a trailing .*, if it does we
# want to remove before parsing.
if operator == "==" and version.endswith(".*"):
@ -694,12 +694,18 @@ class SpecifierSet(BaseSpecifier):
specifiers (``>=3.0,!=3.1``), or no specifier at all.
"""
def __init__(self, specifiers: str = "", prereleases: bool | None = None) -> None:
def __init__(
self,
specifiers: str | Iterable[Specifier] = "",
prereleases: bool | None = None,
) -> None:
"""Initialize a SpecifierSet instance.
:param specifiers:
The string representation of a specifier or a comma-separated list of
specifiers which will be parsed and normalized before use.
May also be an iterable of ``Specifier`` instances, which will be used
as is.
:param prereleases:
This tells the SpecifierSet if it should accept prerelease versions if
applicable or not. The default of ``None`` will autodetect it from the
@ -710,12 +716,17 @@ class SpecifierSet(BaseSpecifier):
raised.
"""
# Split on `,` to break each individual specifier into it's own item, and
# strip each item to remove leading/trailing whitespace.
split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
if isinstance(specifiers, str):
# Split on `,` to break each individual specifier into its own item, and
# strip each item to remove leading/trailing whitespace.
split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
# Make each individual specifier a Specifier and save in a frozen set for later.
self._specs = frozenset(map(Specifier, split_specifiers))
# Make each individual specifier a Specifier and save in a frozen set
# for later.
self._specs = frozenset(map(Specifier, split_specifiers))
else:
# Save the supplied specifiers in a frozen set.
self._specs = frozenset(specifiers)
# Store our prereleases value so we can use it later to determine if
# we accept prereleases or not.

View File

@ -47,7 +47,7 @@ class Tag:
is also supported.
"""
__slots__ = ["_interpreter", "_abi", "_platform", "_hash"]
__slots__ = ["_abi", "_hash", "_interpreter", "_platform"]
def __init__(self, interpreter: str, abi: str, platform: str) -> None:
self._interpreter = interpreter.lower()
@ -235,9 +235,8 @@ def cpython_tags(
if use_abi3:
for minor_version in range(python_version[1] - 1, 1, -1):
for platform_ in platforms:
interpreter = "cp{version}".format(
version=_version_nodot((python_version[0], minor_version))
)
version = _version_nodot((python_version[0], minor_version))
interpreter = f"cp{version}"
yield Tag(interpreter, "abi3", platform_)
@ -435,24 +434,22 @@ def mac_platforms(
if (10, 0) <= version and version < (11, 0):
# Prior to Mac OS 11, each yearly release of Mac OS bumped the
# "minor" version number. The major version was always 10.
major_version = 10
for minor_version in range(version[1], -1, -1):
compat_version = 10, minor_version
compat_version = major_version, minor_version
binary_formats = _mac_binary_formats(compat_version, arch)
for binary_format in binary_formats:
yield "macosx_{major}_{minor}_{binary_format}".format(
major=10, minor=minor_version, binary_format=binary_format
)
yield f"macosx_{major_version}_{minor_version}_{binary_format}"
if version >= (11, 0):
# Starting with Mac OS 11, each yearly release bumps the major version
# number. The minor versions are now the midyear updates.
minor_version = 0
for major_version in range(version[0], 10, -1):
compat_version = major_version, 0
compat_version = major_version, minor_version
binary_formats = _mac_binary_formats(compat_version, arch)
for binary_format in binary_formats:
yield "macosx_{major}_{minor}_{binary_format}".format(
major=major_version, minor=0, binary_format=binary_format
)
yield f"macosx_{major_version}_{minor_version}_{binary_format}"
if version >= (11, 0):
# Mac OS 11 on x86_64 is compatible with binaries from previous releases.
@ -462,25 +459,18 @@ def mac_platforms(
# However, the "universal2" binary format can have a
# macOS version earlier than 11.0 when the x86_64 part of the binary supports
# that version of macOS.
major_version = 10
if arch == "x86_64":
for minor_version in range(16, 3, -1):
compat_version = 10, minor_version
compat_version = major_version, minor_version
binary_formats = _mac_binary_formats(compat_version, arch)
for binary_format in binary_formats:
yield "macosx_{major}_{minor}_{binary_format}".format(
major=compat_version[0],
minor=compat_version[1],
binary_format=binary_format,
)
yield f"macosx_{major_version}_{minor_version}_{binary_format}"
else:
for minor_version in range(16, 3, -1):
compat_version = 10, minor_version
compat_version = major_version, minor_version
binary_format = "universal2"
yield "macosx_{major}_{minor}_{binary_format}".format(
major=compat_version[0],
minor=compat_version[1],
binary_format=binary_format,
)
yield f"macosx_{major_version}_{minor_version}_{binary_format}"
def ios_platforms(
@ -500,7 +490,7 @@ def ios_platforms(
# if iOS is the current platform, ios_ver *must* be defined. However,
# it won't exist for CPython versions before 3.13, which causes a mypy
# error.
_, release, _, _ = platform.ios_ver() # type: ignore[attr-defined]
_, release, _, _ = platform.ios_ver() # type: ignore[attr-defined, unused-ignore]
version = cast("AppleVersion", tuple(map(int, release.split(".")[:2])))
if multiarch is None:

View File

@ -4,11 +4,12 @@
from __future__ import annotations
import functools
import re
from typing import NewType, Tuple, Union, cast
from .tags import Tag, parse_tag
from .version import InvalidVersion, Version
from .version import InvalidVersion, Version, _TrimmedRelease
BuildTag = Union[Tuple[()], Tuple[int, str]]
NormalizedName = NewType("NormalizedName", str)
@ -54,52 +55,40 @@ def is_normalized_name(name: str) -> bool:
return _normalized_regex.match(name) is not None
@functools.singledispatch
def canonicalize_version(
version: Version | str, *, strip_trailing_zero: bool = True
) -> str:
"""
This is very similar to Version.__str__, but has one subtle difference
with the way it handles the release segment.
Return a canonical form of a version as a string.
>>> canonicalize_version('1.0.1')
'1.0.1'
Per PEP 625, versions may have multiple canonical forms, differing
only by trailing zeros.
>>> canonicalize_version('1.0.0')
'1'
>>> canonicalize_version('1.0.0', strip_trailing_zero=False)
'1.0.0'
Invalid versions are returned unaltered.
>>> canonicalize_version('foo bar baz')
'foo bar baz'
"""
if isinstance(version, str):
try:
parsed = Version(version)
except InvalidVersion:
# Legacy versions cannot be normalized
return version
else:
parsed = version
return str(_TrimmedRelease(str(version)) if strip_trailing_zero else version)
parts = []
# Epoch
if parsed.epoch != 0:
parts.append(f"{parsed.epoch}!")
# Release segment
release_segment = ".".join(str(x) for x in parsed.release)
if strip_trailing_zero:
# NB: This strips trailing '.0's to normalize
release_segment = re.sub(r"(\.0)+$", "", release_segment)
parts.append(release_segment)
# Pre-release
if parsed.pre is not None:
parts.append("".join(str(x) for x in parsed.pre))
# Post-release
if parsed.post is not None:
parts.append(f".post{parsed.post}")
# Development release
if parsed.dev is not None:
parts.append(f".dev{parsed.dev}")
# Local version segment
if parsed.local is not None:
parts.append(f"+{parsed.local}")
return "".join(parts)
@canonicalize_version.register
def _(version: str, *, strip_trailing_zero: bool = True) -> str:
try:
parsed = Version(version)
except InvalidVersion:
# Legacy versions cannot be normalized
return version
return canonicalize_version(parsed, strip_trailing_zero=strip_trailing_zero)
def parse_wheel_filename(
@ -107,28 +96,28 @@ def parse_wheel_filename(
) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]:
if not filename.endswith(".whl"):
raise InvalidWheelFilename(
f"Invalid wheel filename (extension must be '.whl'): {filename}"
f"Invalid wheel filename (extension must be '.whl'): {filename!r}"
)
filename = filename[:-4]
dashes = filename.count("-")
if dashes not in (4, 5):
raise InvalidWheelFilename(
f"Invalid wheel filename (wrong number of parts): {filename}"
f"Invalid wheel filename (wrong number of parts): {filename!r}"
)
parts = filename.split("-", dashes - 2)
name_part = parts[0]
# See PEP 427 for the rules on escaping the project name.
if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None:
raise InvalidWheelFilename(f"Invalid project name: {filename}")
raise InvalidWheelFilename(f"Invalid project name: {filename!r}")
name = canonicalize_name(name_part)
try:
version = Version(parts[1])
except InvalidVersion as e:
raise InvalidWheelFilename(
f"Invalid wheel filename (invalid version): {filename}"
f"Invalid wheel filename (invalid version): {filename!r}"
) from e
if dashes == 5:
@ -136,7 +125,7 @@ def parse_wheel_filename(
build_match = _build_tag_regex.match(build_part)
if build_match is None:
raise InvalidWheelFilename(
f"Invalid build number: {build_part} in '{filename}'"
f"Invalid build number: {build_part} in {filename!r}"
)
build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2)))
else:
@ -153,14 +142,14 @@ def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]:
else:
raise InvalidSdistFilename(
f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):"
f" {filename}"
f" {filename!r}"
)
# We are requiring a PEP 440 version, which cannot contain dashes,
# so we split on the last dash.
name_part, sep, version_part = file_stem.rpartition("-")
if not sep:
raise InvalidSdistFilename(f"Invalid sdist filename: {filename}")
raise InvalidSdistFilename(f"Invalid sdist filename: {filename!r}")
name = canonicalize_name(name_part)
@ -168,7 +157,7 @@ def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]:
version = Version(version_part)
except InvalidVersion as e:
raise InvalidSdistFilename(
f"Invalid sdist filename (invalid version): {filename}"
f"Invalid sdist filename (invalid version): {filename!r}"
) from e
return (name, version)

View File

@ -15,7 +15,7 @@ from typing import Any, Callable, NamedTuple, SupportsInt, Tuple, Union
from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType
__all__ = ["VERSION_PATTERN", "parse", "Version", "InvalidVersion"]
__all__ = ["VERSION_PATTERN", "InvalidVersion", "Version", "parse"]
LocalType = Tuple[Union[int, str], ...]
@ -199,7 +199,7 @@ class Version(_BaseVersion):
# Validate the version and parse it into pieces
match = self._regex.search(version)
if not match:
raise InvalidVersion(f"Invalid version: '{version}'")
raise InvalidVersion(f"Invalid version: {version!r}")
# Store the parsed out pieces of the version
self._version = _Version(
@ -232,7 +232,7 @@ class Version(_BaseVersion):
return f"<Version('{self}')>"
def __str__(self) -> str:
"""A string representation of the version that can be rounded-tripped.
"""A string representation of the version that can be round-tripped.
>>> str(Version("1.0a5"))
'1.0a5'
@ -350,8 +350,8 @@ class Version(_BaseVersion):
'1.2.3'
>>> Version("1.2.3+abc").public
'1.2.3'
>>> Version("1.2.3+abc.dev1").public
'1.2.3'
>>> Version("1!1.2.3dev1+abc").public
'1!1.2.3.dev1'
"""
return str(self).split("+", 1)[0]
@ -363,7 +363,7 @@ class Version(_BaseVersion):
'1.2.3'
>>> Version("1.2.3+abc").base_version
'1.2.3'
>>> Version("1!1.2.3+abc.dev1").base_version
>>> Version("1!1.2.3dev1+abc").base_version
'1!1.2.3'
The "base version" is the public version of the project without any pre or post
@ -451,6 +451,23 @@ class Version(_BaseVersion):
return self.release[2] if len(self.release) >= 3 else 0
class _TrimmedRelease(Version):
@property
def release(self) -> tuple[int, ...]:
"""
Release segment without any trailing zeros.
>>> _TrimmedRelease('1.0.0').release
(1,)
>>> _TrimmedRelease('0.0').release
(0,)
"""
rel = super().release
nonzeros = (index for index, val in enumerate(rel) if val)
last_nonzero = max(nonzeros, default=0)
return rel[: last_nonzero + 1]
def _parse_letter_version(
letter: str | None, number: str | bytes | SupportsInt | None
) -> tuple[str, int] | None:
@ -476,7 +493,9 @@ def _parse_letter_version(
letter = "post"
return letter, int(number)
if not letter and number:
assert not letter
if number:
# We assume if we are given a number, but we are not given a letter
# then this is using the implicit post release syntax (e.g. 1.0-1)
letter = "post"

View File

@ -19,18 +19,18 @@ if TYPE_CHECKING:
from pathlib import Path
from typing import Literal
if sys.platform == "win32":
from pip._vendor.platformdirs.windows import Windows as _Result
elif sys.platform == "darwin":
from pip._vendor.platformdirs.macos import MacOS as _Result
else:
from pip._vendor.platformdirs.unix import Unix as _Result
def _set_platform_dir_class() -> type[PlatformDirsABC]:
if sys.platform == "win32":
from pip._vendor.platformdirs.windows import Windows as Result # noqa: PLC0415
elif sys.platform == "darwin":
from pip._vendor.platformdirs.macos import MacOS as Result # noqa: PLC0415
else:
from pip._vendor.platformdirs.unix import Unix as Result # noqa: PLC0415
if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system":
if os.getenv("SHELL") or os.getenv("PREFIX"):
return Result
return _Result
from pip._vendor.platformdirs.android import _android_folder # noqa: PLC0415
@ -39,10 +39,14 @@ def _set_platform_dir_class() -> type[PlatformDirsABC]:
return Android # return to avoid redefinition of a result
return Result
return _Result
PlatformDirs = _set_platform_dir_class() #: Currently active platform
if TYPE_CHECKING:
# Work around mypy issue: https://github.com/python/mypy/issues/10962
PlatformDirs = _Result
else:
PlatformDirs = _set_platform_dir_class() #: Currently active platform
AppDirs = PlatformDirs #: Backwards compatibility with appdirs

View File

@ -117,7 +117,7 @@ class Android(PlatformDirsABC):
@lru_cache(maxsize=1)
def _android_folder() -> str | None: # noqa: C901, PLR0912
def _android_folder() -> str | None: # noqa: C901
""":return: base folder for the Android OS or None if it cannot be found"""
result: str | None = None
# type checker isn't happy with our "import android", just don't do this when type checking see

View File

@ -91,6 +91,12 @@ class PlatformDirsABC(ABC): # noqa: PLR0904
if self.ensure_exists:
Path(path).mkdir(parents=True, exist_ok=True)
def _first_item_as_path_if_multipath(self, directory: str) -> Path:
if self.multipath:
# If multipath is True, the first path is returned.
directory = directory.split(os.pathsep)[0]
return Path(directory)
@property
@abstractmethod
def user_data_dir(self) -> str:

View File

@ -4,9 +4,13 @@ from __future__ import annotations
import os.path
import sys
from typing import TYPE_CHECKING
from .api import PlatformDirsABC
if TYPE_CHECKING:
from pathlib import Path
class MacOS(PlatformDirsABC):
"""
@ -42,6 +46,11 @@ class MacOS(PlatformDirsABC):
return os.pathsep.join(path_list)
return path_list[0]
@property
def site_data_path(self) -> Path:
""":return: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
return self._first_item_as_path_if_multipath(self.site_data_dir)
@property
def user_config_dir(self) -> str:
""":return: config directory tied to the user, same as `user_data_dir`"""
@ -74,6 +83,11 @@ class MacOS(PlatformDirsABC):
return os.pathsep.join(path_list)
return path_list[0]
@property
def site_cache_path(self) -> Path:
""":return: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
return self._first_item_as_path_if_multipath(self.site_cache_dir)
@property
def user_state_dir(self) -> str:
""":return: state directory tied to the user, same as `user_data_dir`"""

View File

@ -218,12 +218,6 @@ class Unix(PlatformDirsABC): # noqa: PLR0904
""":return: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
return self._first_item_as_path_if_multipath(self.site_cache_dir)
def _first_item_as_path_if_multipath(self, directory: str) -> Path:
if self.multipath:
# If multipath is True, the first path is returned.
directory = directory.split(os.pathsep)[0]
return Path(directory)
def iter_config_dirs(self) -> Iterator[str]:
""":yield: all user and site configuration directories."""
yield self.user_config_dir

View File

@ -12,5 +12,5 @@ __version__: str
__version_tuple__: VERSION_TUPLE
version_tuple: VERSION_TUPLE
__version__ = version = '4.2.2'
__version_tuple__ = version_tuple = (4, 2, 2)
__version__ = version = '4.3.6'
__version_tuple__ = version_tuple = (4, 3, 6)

View File

@ -1,8 +1,9 @@
"""Wrappers to call pyproject.toml-based build backend hooks.
"""
from typing import TYPE_CHECKING
from ._impl import (
BackendInvalid,
BackendUnavailable,
BuildBackendHookCaller,
HookMissing,
@ -11,13 +12,20 @@ from ._impl import (
quiet_subprocess_runner,
)
__version__ = '1.0.0'
__version__ = "1.2.0"
__all__ = [
'BackendUnavailable',
'BackendInvalid',
'HookMissing',
'UnsupportedOperation',
'default_subprocess_runner',
'quiet_subprocess_runner',
'BuildBackendHookCaller',
"BackendUnavailable",
"BackendInvalid",
"HookMissing",
"UnsupportedOperation",
"default_subprocess_runner",
"quiet_subprocess_runner",
"BuildBackendHookCaller",
]
BackendInvalid = BackendUnavailable # Deprecated alias, previously a separate exception
if TYPE_CHECKING:
from ._impl import SubprocessRunner
__all__ += ["SubprocessRunner"]

View File

@ -1,8 +0,0 @@
__all__ = ("tomllib",)
import sys
if sys.version_info >= (3, 11):
import tomllib
else:
from pip._vendor import tomli as tomllib

View File

@ -6,48 +6,72 @@ from contextlib import contextmanager
from os.path import abspath
from os.path import join as pjoin
from subprocess import STDOUT, check_call, check_output
from typing import TYPE_CHECKING, Any, Iterator, Mapping, Optional, Sequence
from ._in_process import _in_proc_script_path
if TYPE_CHECKING:
from typing import Protocol
def write_json(obj, path, **kwargs):
with open(path, 'w', encoding='utf-8') as f:
class SubprocessRunner(Protocol):
"""A protocol for the subprocess runner."""
def __call__(
self,
cmd: Sequence[str],
cwd: Optional[str] = None,
extra_environ: Optional[Mapping[str, str]] = None,
) -> None:
...
def write_json(obj: Mapping[str, Any], path: str, **kwargs) -> None:
with open(path, "w", encoding="utf-8") as f:
json.dump(obj, f, **kwargs)
def read_json(path):
with open(path, encoding='utf-8') as f:
def read_json(path: str) -> Mapping[str, Any]:
with open(path, encoding="utf-8") as f:
return json.load(f)
class BackendUnavailable(Exception):
"""Will be raised if the backend cannot be imported in the hook process."""
def __init__(self, traceback):
self.traceback = traceback
class BackendInvalid(Exception):
"""Will be raised if the backend is invalid."""
def __init__(self, backend_name, backend_path, message):
super().__init__(message)
def __init__(
self,
traceback: str,
message: Optional[str] = None,
backend_name: Optional[str] = None,
backend_path: Optional[Sequence[str]] = None,
) -> None:
# Preserving arg order for the sake of API backward compatibility.
self.backend_name = backend_name
self.backend_path = backend_path
self.traceback = traceback
super().__init__(message or "Error while importing backend")
class HookMissing(Exception):
"""Will be raised on missing hooks (if a fallback can't be used)."""
def __init__(self, hook_name):
def __init__(self, hook_name: str) -> None:
super().__init__(hook_name)
self.hook_name = hook_name
class UnsupportedOperation(Exception):
"""May be raised by build_sdist if the backend indicates that it can't."""
def __init__(self, traceback):
def __init__(self, traceback: str) -> None:
self.traceback = traceback
def default_subprocess_runner(cmd, cwd=None, extra_environ=None):
def default_subprocess_runner(
cmd: Sequence[str],
cwd: Optional[str] = None,
extra_environ: Optional[Mapping[str, str]] = None,
) -> None:
"""The default method of calling the wrapper subprocess.
This uses :func:`subprocess.check_call` under the hood.
@ -59,7 +83,11 @@ def default_subprocess_runner(cmd, cwd=None, extra_environ=None):
check_call(cmd, cwd=cwd, env=env)
def quiet_subprocess_runner(cmd, cwd=None, extra_environ=None):
def quiet_subprocess_runner(
cmd: Sequence[str],
cwd: Optional[str] = None,
extra_environ: Optional[Mapping[str, str]] = None,
) -> None:
"""Call the subprocess while suppressing output.
This uses :func:`subprocess.check_output` under the hood.
@ -71,7 +99,7 @@ def quiet_subprocess_runner(cmd, cwd=None, extra_environ=None):
check_output(cmd, cwd=cwd, env=env, stderr=STDOUT)
def norm_and_check(source_tree, requested):
def norm_and_check(source_tree: str, requested: str) -> str:
"""Normalise and check a backend path.
Ensure that the requested backend path is specified as a relative path,
@ -96,17 +124,16 @@ def norm_and_check(source_tree, requested):
class BuildBackendHookCaller:
"""A wrapper to call the build backend hooks for a source directory.
"""
"""A wrapper to call the build backend hooks for a source directory."""
def __init__(
self,
source_dir,
build_backend,
backend_path=None,
runner=None,
python_executable=None,
):
self,
source_dir: str,
build_backend: str,
backend_path: Optional[Sequence[str]] = None,
runner: Optional["SubprocessRunner"] = None,
python_executable: Optional[str] = None,
) -> None:
"""
:param source_dir: The source directory to invoke the build backend for
:param build_backend: The build backend spec
@ -121,9 +148,7 @@ class BuildBackendHookCaller:
self.source_dir = abspath(source_dir)
self.build_backend = build_backend
if backend_path:
backend_path = [
norm_and_check(self.source_dir, p) for p in backend_path
]
backend_path = [norm_and_check(self.source_dir, p) for p in backend_path]
self.backend_path = backend_path
self._subprocess_runner = runner
if not python_executable:
@ -131,10 +156,12 @@ class BuildBackendHookCaller:
self.python_executable = python_executable
@contextmanager
def subprocess_runner(self, runner):
def subprocess_runner(self, runner: "SubprocessRunner") -> Iterator[None]:
"""A context manager for temporarily overriding the default
:ref:`subprocess runner <Subprocess Runners>`.
:param runner: The new subprocess runner to use within the context.
.. code-block:: python
hook_caller = BuildBackendHookCaller(...)
@ -148,33 +175,44 @@ class BuildBackendHookCaller:
finally:
self._subprocess_runner = prev
def _supported_features(self):
def _supported_features(self) -> Sequence[str]:
"""Return the list of optional features supported by the backend."""
return self._call_hook('_supported_features', {})
return self._call_hook("_supported_features", {})
def get_requires_for_build_wheel(self, config_settings=None):
def get_requires_for_build_wheel(
self,
config_settings: Optional[Mapping[str, Any]] = None,
) -> Sequence[str]:
"""Get additional dependencies required for building a wheel.
:param config_settings: The configuration settings for the build backend
:returns: A list of :pep:`dependency specifiers <508>`.
:rtype: list[str]
.. admonition:: Fallback
If the build backend does not defined a hook with this name, an
empty list will be returned.
"""
return self._call_hook('get_requires_for_build_wheel', {
'config_settings': config_settings
})
return self._call_hook(
"get_requires_for_build_wheel", {"config_settings": config_settings}
)
def prepare_metadata_for_build_wheel(
self, metadata_directory, config_settings=None,
_allow_fallback=True):
self,
metadata_directory: str,
config_settings: Optional[Mapping[str, Any]] = None,
_allow_fallback: bool = True,
) -> str:
"""Prepare a ``*.dist-info`` folder with metadata for this project.
:param metadata_directory: The directory to write the metadata to
:param config_settings: The configuration settings for the build backend
:param _allow_fallback:
Whether to allow the fallback to building a wheel and extracting
the metadata from it. Should be passed as a keyword argument only.
:returns: Name of the newly created subfolder within
``metadata_directory``, containing the metadata.
:rtype: str
.. admonition:: Fallback
@ -183,17 +221,26 @@ class BuildBackendHookCaller:
wheel via the ``build_wheel`` hook and the dist-info extracted from
that will be returned.
"""
return self._call_hook('prepare_metadata_for_build_wheel', {
'metadata_directory': abspath(metadata_directory),
'config_settings': config_settings,
'_allow_fallback': _allow_fallback,
})
return self._call_hook(
"prepare_metadata_for_build_wheel",
{
"metadata_directory": abspath(metadata_directory),
"config_settings": config_settings,
"_allow_fallback": _allow_fallback,
},
)
def build_wheel(
self, wheel_directory, config_settings=None,
metadata_directory=None):
self,
wheel_directory: str,
config_settings: Optional[Mapping[str, Any]] = None,
metadata_directory: Optional[str] = None,
) -> str:
"""Build a wheel from this project.
:param wheel_directory: The directory to write the wheel to
:param config_settings: The configuration settings for the build backend
:param metadata_directory: The directory to reuse existing metadata from
:returns:
The name of the newly created wheel within ``wheel_directory``.
@ -206,35 +253,48 @@ class BuildBackendHookCaller:
"""
if metadata_directory is not None:
metadata_directory = abspath(metadata_directory)
return self._call_hook('build_wheel', {
'wheel_directory': abspath(wheel_directory),
'config_settings': config_settings,
'metadata_directory': metadata_directory,
})
return self._call_hook(
"build_wheel",
{
"wheel_directory": abspath(wheel_directory),
"config_settings": config_settings,
"metadata_directory": metadata_directory,
},
)
def get_requires_for_build_editable(self, config_settings=None):
def get_requires_for_build_editable(
self,
config_settings: Optional[Mapping[str, Any]] = None,
) -> Sequence[str]:
"""Get additional dependencies required for building an editable wheel.
:param config_settings: The configuration settings for the build backend
:returns: A list of :pep:`dependency specifiers <508>`.
:rtype: list[str]
.. admonition:: Fallback
If the build backend does not defined a hook with this name, an
empty list will be returned.
"""
return self._call_hook('get_requires_for_build_editable', {
'config_settings': config_settings
})
return self._call_hook(
"get_requires_for_build_editable", {"config_settings": config_settings}
)
def prepare_metadata_for_build_editable(
self, metadata_directory, config_settings=None,
_allow_fallback=True):
self,
metadata_directory: str,
config_settings: Optional[Mapping[str, Any]] = None,
_allow_fallback: bool = True,
) -> Optional[str]:
"""Prepare a ``*.dist-info`` folder with metadata for this project.
:param metadata_directory: The directory to write the metadata to
:param config_settings: The configuration settings for the build backend
:param _allow_fallback:
Whether to allow the fallback to building a wheel and extracting
the metadata from it. Should be passed as a keyword argument only.
:returns: Name of the newly created subfolder within
``metadata_directory``, containing the metadata.
:rtype: str
.. admonition:: Fallback
@ -243,17 +303,26 @@ class BuildBackendHookCaller:
wheel via the ``build_editable`` hook and the dist-info
extracted from that will be returned.
"""
return self._call_hook('prepare_metadata_for_build_editable', {
'metadata_directory': abspath(metadata_directory),
'config_settings': config_settings,
'_allow_fallback': _allow_fallback,
})
return self._call_hook(
"prepare_metadata_for_build_editable",
{
"metadata_directory": abspath(metadata_directory),
"config_settings": config_settings,
"_allow_fallback": _allow_fallback,
},
)
def build_editable(
self, wheel_directory, config_settings=None,
metadata_directory=None):
self,
wheel_directory: str,
config_settings: Optional[Mapping[str, Any]] = None,
metadata_directory: Optional[str] = None,
) -> str:
"""Build an editable wheel from this project.
:param wheel_directory: The directory to write the wheel to
:param config_settings: The configuration settings for the build backend
:param metadata_directory: The directory to reuse existing metadata from
:returns:
The name of the newly created wheel within ``wheel_directory``.
@ -267,43 +336,55 @@ class BuildBackendHookCaller:
"""
if metadata_directory is not None:
metadata_directory = abspath(metadata_directory)
return self._call_hook('build_editable', {
'wheel_directory': abspath(wheel_directory),
'config_settings': config_settings,
'metadata_directory': metadata_directory,
})
return self._call_hook(
"build_editable",
{
"wheel_directory": abspath(wheel_directory),
"config_settings": config_settings,
"metadata_directory": metadata_directory,
},
)
def get_requires_for_build_sdist(self, config_settings=None):
def get_requires_for_build_sdist(
self,
config_settings: Optional[Mapping[str, Any]] = None,
) -> Sequence[str]:
"""Get additional dependencies required for building an sdist.
:returns: A list of :pep:`dependency specifiers <508>`.
:rtype: list[str]
"""
return self._call_hook('get_requires_for_build_sdist', {
'config_settings': config_settings
})
return self._call_hook(
"get_requires_for_build_sdist", {"config_settings": config_settings}
)
def build_sdist(self, sdist_directory, config_settings=None):
def build_sdist(
self,
sdist_directory: str,
config_settings: Optional[Mapping[str, Any]] = None,
) -> str:
"""Build an sdist from this project.
:returns:
The name of the newly created sdist within ``wheel_directory``.
"""
return self._call_hook('build_sdist', {
'sdist_directory': abspath(sdist_directory),
'config_settings': config_settings,
})
return self._call_hook(
"build_sdist",
{
"sdist_directory": abspath(sdist_directory),
"config_settings": config_settings,
},
)
def _call_hook(self, hook_name, kwargs):
extra_environ = {'PEP517_BUILD_BACKEND': self.build_backend}
def _call_hook(self, hook_name: str, kwargs: Mapping[str, Any]) -> Any:
extra_environ = {"_PYPROJECT_HOOKS_BUILD_BACKEND": self.build_backend}
if self.backend_path:
backend_path = os.pathsep.join(self.backend_path)
extra_environ['PEP517_BACKEND_PATH'] = backend_path
extra_environ["_PYPROJECT_HOOKS_BACKEND_PATH"] = backend_path
with tempfile.TemporaryDirectory() as td:
hook_input = {'kwargs': kwargs}
write_json(hook_input, pjoin(td, 'input.json'), indent=2)
hook_input = {"kwargs": kwargs}
write_json(hook_input, pjoin(td, "input.json"), indent=2)
# Run the hook in a subprocess
with _in_proc_script_path() as script:
@ -311,20 +392,19 @@ class BuildBackendHookCaller:
self._subprocess_runner(
[python, abspath(str(script)), hook_name, td],
cwd=self.source_dir,
extra_environ=extra_environ
extra_environ=extra_environ,
)
data = read_json(pjoin(td, 'output.json'))
if data.get('unsupported'):
raise UnsupportedOperation(data.get('traceback', ''))
if data.get('no_backend'):
raise BackendUnavailable(data.get('traceback', ''))
if data.get('backend_invalid'):
raise BackendInvalid(
data = read_json(pjoin(td, "output.json"))
if data.get("unsupported"):
raise UnsupportedOperation(data.get("traceback", ""))
if data.get("no_backend"):
raise BackendUnavailable(
data.get("traceback", ""),
message=data.get("backend_error", ""),
backend_name=self.build_backend,
backend_path=self.backend_path,
message=data.get('backend_error', '')
)
if data.get('hook_missing'):
raise HookMissing(data.get('missing_hook_name') or hook_name)
return data['return_val']
if data.get("hook_missing"):
raise HookMissing(data.get("missing_hook_name") or hook_name)
return data["return_val"]

View File

@ -11,8 +11,11 @@ try:
except AttributeError:
# Python 3.8 compatibility
def _in_proc_script_path():
return resources.path(__package__, '_in_process.py')
return resources.path(__package__, "_in_process.py")
else:
def _in_proc_script_path():
return resources.as_file(
resources.files(__package__).joinpath('_in_process.py'))
resources.files(__package__).joinpath("_in_process.py")
)

View File

@ -3,8 +3,8 @@
It expects:
- Command line args: hook_name, control_dir
- Environment variables:
PEP517_BUILD_BACKEND=entry.point:spec
PEP517_BACKEND_PATH=paths (separated with os.pathsep)
_PYPROJECT_HOOKS_BUILD_BACKEND=entry.point:spec
_PYPROJECT_HOOKS_BACKEND_PATH=paths (separated with os.pathsep)
- control_dir/input.json:
- {"kwargs": {...}}
@ -21,6 +21,7 @@ import sys
import traceback
from glob import glob
from importlib import import_module
from importlib.machinery import PathFinder
from os.path import join as pjoin
# This file is run as a script, and `import wrappers` is not zip-safe, so we
@ -28,69 +29,93 @@ from os.path import join as pjoin
def write_json(obj, path, **kwargs):
with open(path, 'w', encoding='utf-8') as f:
with open(path, "w", encoding="utf-8") as f:
json.dump(obj, f, **kwargs)
def read_json(path):
with open(path, encoding='utf-8') as f:
with open(path, encoding="utf-8") as f:
return json.load(f)
class BackendUnavailable(Exception):
"""Raised if we cannot import the backend"""
def __init__(self, traceback):
self.traceback = traceback
class BackendInvalid(Exception):
"""Raised if the backend is invalid"""
def __init__(self, message):
def __init__(self, message, traceback=None):
super().__init__(message)
self.message = message
self.traceback = traceback
class HookMissing(Exception):
"""Raised if a hook is missing and we are not executing the fallback"""
def __init__(self, hook_name=None):
super().__init__(hook_name)
self.hook_name = hook_name
def contained_in(filename, directory):
"""Test if a file is located within the given directory."""
filename = os.path.normcase(os.path.abspath(filename))
directory = os.path.normcase(os.path.abspath(directory))
return os.path.commonprefix([filename, directory]) == directory
def _build_backend():
"""Find and load the build backend"""
# Add in-tree backend directories to the front of sys.path.
backend_path = os.environ.get('PEP517_BACKEND_PATH')
if backend_path:
extra_pathitems = backend_path.split(os.pathsep)
sys.path[:0] = extra_pathitems
backend_path = os.environ.get("_PYPROJECT_HOOKS_BACKEND_PATH")
ep = os.environ["_PYPROJECT_HOOKS_BUILD_BACKEND"]
mod_path, _, obj_path = ep.partition(":")
if backend_path:
# Ensure in-tree backend directories have the highest priority when importing.
extra_pathitems = backend_path.split(os.pathsep)
sys.meta_path.insert(0, _BackendPathFinder(extra_pathitems, mod_path))
ep = os.environ['PEP517_BUILD_BACKEND']
mod_path, _, obj_path = ep.partition(':')
try:
obj = import_module(mod_path)
except ImportError:
raise BackendUnavailable(traceback.format_exc())
if backend_path:
if not any(
contained_in(obj.__file__, path)
for path in extra_pathitems
):
raise BackendInvalid("Backend was not loaded from backend-path")
msg = f"Cannot import {mod_path!r}"
raise BackendUnavailable(msg, traceback.format_exc())
if obj_path:
for path_part in obj_path.split('.'):
for path_part in obj_path.split("."):
obj = getattr(obj, path_part)
return obj
class _BackendPathFinder:
"""Implements the MetaPathFinder interface to locate modules in ``backend-path``.
Since the environment provided by the frontend can contain all sorts of
MetaPathFinders, the only way to ensure the backend is loaded from the
right place is to prepend our own.
"""
def __init__(self, backend_path, backend_module):
self.backend_path = backend_path
self.backend_module = backend_module
self.backend_parent, _, _ = backend_module.partition(".")
def find_spec(self, fullname, _path, _target=None):
if "." in fullname:
# Rely on importlib to find nested modules based on parent's path
return None
# Ignore other items in _path or sys.path and use backend_path instead:
spec = PathFinder.find_spec(fullname, path=self.backend_path)
if spec is None and fullname == self.backend_parent:
# According to the spec, the backend MUST be loaded from backend-path.
# Therefore, we can halt the import machinery and raise a clean error.
msg = f"Cannot find module {self.backend_module!r} in {self.backend_path!r}"
raise BackendUnavailable(msg)
return spec
if sys.version_info >= (3, 8):
def find_distributions(self, context=None):
# Delayed import: Python 3.7 does not contain importlib.metadata
from importlib.metadata import DistributionFinder, MetadataPathFinder
context = DistributionFinder.Context(path=self.backend_path)
return MetadataPathFinder.find_distributions(context=context)
def _supported_features():
"""Return the list of options features supported by the backend.
@ -133,7 +158,8 @@ def get_requires_for_build_editable(config_settings):
def prepare_metadata_for_build_wheel(
metadata_directory, config_settings, _allow_fallback):
metadata_directory, config_settings, _allow_fallback
):
"""Invoke optional prepare_metadata_for_build_wheel
Implements a fallback by building a wheel if the hook isn't defined,
@ -150,12 +176,14 @@ def prepare_metadata_for_build_wheel(
# fallback to build_wheel outside the try block to avoid exception chaining
# which can be confusing to users and is not relevant
whl_basename = backend.build_wheel(metadata_directory, config_settings)
return _get_wheel_metadata_from_wheel(whl_basename, metadata_directory,
config_settings)
return _get_wheel_metadata_from_wheel(
whl_basename, metadata_directory, config_settings
)
def prepare_metadata_for_build_editable(
metadata_directory, config_settings, _allow_fallback):
metadata_directory, config_settings, _allow_fallback
):
"""Invoke optional prepare_metadata_for_build_editable
Implements a fallback by building an editable wheel if the hook isn't
@ -171,24 +199,24 @@ def prepare_metadata_for_build_editable(
try:
build_hook = backend.build_editable
except AttributeError:
raise HookMissing(hook_name='build_editable')
raise HookMissing(hook_name="build_editable")
else:
whl_basename = build_hook(metadata_directory, config_settings)
return _get_wheel_metadata_from_wheel(whl_basename,
metadata_directory,
config_settings)
return _get_wheel_metadata_from_wheel(
whl_basename, metadata_directory, config_settings
)
else:
return hook(metadata_directory, config_settings)
WHEEL_BUILT_MARKER = 'PEP517_ALREADY_BUILT_WHEEL'
WHEEL_BUILT_MARKER = "PYPROJECT_HOOKS_ALREADY_BUILT_WHEEL"
def _dist_info_files(whl_zip):
"""Identify the .dist-info folder inside a wheel ZipFile."""
res = []
for path in whl_zip.namelist():
m = re.match(r'[^/\\]+-[^/\\]+\.dist-info/', path)
m = re.match(r"[^/\\]+-[^/\\]+\.dist-info/", path)
if m:
res.append(path)
if res:
@ -196,40 +224,41 @@ def _dist_info_files(whl_zip):
raise Exception("No .dist-info folder found in wheel")
def _get_wheel_metadata_from_wheel(
whl_basename, metadata_directory, config_settings):
def _get_wheel_metadata_from_wheel(whl_basename, metadata_directory, config_settings):
"""Extract the metadata from a wheel.
Fallback for when the build backend does not
define the 'get_wheel_metadata' hook.
"""
from zipfile import ZipFile
with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), 'wb'):
with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), "wb"):
pass # Touch marker file
whl_file = os.path.join(metadata_directory, whl_basename)
with ZipFile(whl_file) as zipf:
dist_info = _dist_info_files(zipf)
zipf.extractall(path=metadata_directory, members=dist_info)
return dist_info[0].split('/')[0]
return dist_info[0].split("/")[0]
def _find_already_built_wheel(metadata_directory):
"""Check for a wheel already built during the get_wheel_metadata hook.
"""
"""Check for a wheel already built during the get_wheel_metadata hook."""
if not metadata_directory:
return None
metadata_parent = os.path.dirname(metadata_directory)
if not os.path.isfile(pjoin(metadata_parent, WHEEL_BUILT_MARKER)):
return None
whl_files = glob(os.path.join(metadata_parent, '*.whl'))
whl_files = glob(os.path.join(metadata_parent, "*.whl"))
if not whl_files:
print('Found wheel built marker, but no .whl files')
print("Found wheel built marker, but no .whl files")
return None
if len(whl_files) > 1:
print('Found multiple .whl files; unspecified behaviour. '
'Will call build_wheel.')
print(
"Found multiple .whl files; unspecified behaviour. "
"Will call build_wheel."
)
return None
# Exactly one .whl file
@ -248,8 +277,9 @@ def build_wheel(wheel_directory, config_settings, metadata_directory=None):
shutil.copy2(prebuilt_whl, wheel_directory)
return os.path.basename(prebuilt_whl)
return _build_backend().build_wheel(wheel_directory, config_settings,
metadata_directory)
return _build_backend().build_wheel(
wheel_directory, config_settings, metadata_directory
)
def build_editable(wheel_directory, config_settings, metadata_directory=None):
@ -293,6 +323,7 @@ class _DummyException(Exception):
class GotUnsupportedOperation(Exception):
"""For internal use when backend raises UnsupportedOperation"""
def __init__(self, traceback):
self.traceback = traceback
@ -302,20 +333,20 @@ def build_sdist(sdist_directory, config_settings):
backend = _build_backend()
try:
return backend.build_sdist(sdist_directory, config_settings)
except getattr(backend, 'UnsupportedOperation', _DummyException):
except getattr(backend, "UnsupportedOperation", _DummyException):
raise GotUnsupportedOperation(traceback.format_exc())
HOOK_NAMES = {
'get_requires_for_build_wheel',
'prepare_metadata_for_build_wheel',
'build_wheel',
'get_requires_for_build_editable',
'prepare_metadata_for_build_editable',
'build_editable',
'get_requires_for_build_sdist',
'build_sdist',
'_supported_features',
"get_requires_for_build_wheel",
"prepare_metadata_for_build_wheel",
"build_wheel",
"get_requires_for_build_editable",
"prepare_metadata_for_build_editable",
"build_editable",
"get_requires_for_build_sdist",
"build_sdist",
"_supported_features",
}
@ -326,28 +357,33 @@ def main():
control_dir = sys.argv[2]
if hook_name not in HOOK_NAMES:
sys.exit("Unknown hook: %s" % hook_name)
# Remove the parent directory from sys.path to avoid polluting the backend
# import namespace with this directory.
here = os.path.dirname(__file__)
if here in sys.path:
sys.path.remove(here)
hook = globals()[hook_name]
hook_input = read_json(pjoin(control_dir, 'input.json'))
hook_input = read_json(pjoin(control_dir, "input.json"))
json_out = {'unsupported': False, 'return_val': None}
json_out = {"unsupported": False, "return_val": None}
try:
json_out['return_val'] = hook(**hook_input['kwargs'])
json_out["return_val"] = hook(**hook_input["kwargs"])
except BackendUnavailable as e:
json_out['no_backend'] = True
json_out['traceback'] = e.traceback
except BackendInvalid as e:
json_out['backend_invalid'] = True
json_out['backend_error'] = e.message
json_out["no_backend"] = True
json_out["traceback"] = e.traceback
json_out["backend_error"] = e.message
except GotUnsupportedOperation as e:
json_out['unsupported'] = True
json_out['traceback'] = e.traceback
json_out["unsupported"] = True
json_out["traceback"] = e.traceback
except HookMissing as e:
json_out['hook_missing'] = True
json_out['missing_hook_name'] = e.hook_name or hook_name
json_out["hook_missing"] = True
json_out["missing_hook_name"] = e.hook_name or hook_name
write_json(json_out, pjoin(control_dir, 'output.json'), indent=2)
write_json(json_out, pjoin(control_dir, "output.json"), indent=2)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -11,14 +11,7 @@ If you are packaging Requests, e.g., for a Linux distribution or a managed
environment, you can change the definition of where() to return a separately
packaged CA bundle.
"""
import os
if "_PIP_STANDALONE_CERT" not in os.environ:
from pip._vendor.certifi import where
else:
def where():
return os.environ["_PIP_STANDALONE_CERT"]
from pip._vendor.certifi import where
if __name__ == "__main__":
print(where())

View File

@ -1,5 +1,3 @@
from __future__ import absolute_import
import inspect
from inspect import cleandoc, getdoc, getfile, isclass, ismodule, signature
from typing import Any, Collection, Iterable, Optional, Tuple, Type, Union

View File

@ -46,7 +46,7 @@ class NullFile(IO[str]):
return iter([""])
def __enter__(self) -> IO[str]:
pass
return self
def __exit__(
self,

View File

@ -2,6 +2,7 @@
The API that this module wraps is documented at https://docs.microsoft.com/en-us/windows/console/console-functions
"""
import ctypes
import sys
from typing import Any
@ -380,7 +381,7 @@ class LegacyWindowsTerm:
WindowsCoordinates: The current cursor position.
"""
coord: COORD = GetConsoleScreenBufferInfo(self._handle).dwCursorPosition
return WindowsCoordinates(row=cast(int, coord.Y), col=cast(int, coord.X))
return WindowsCoordinates(row=coord.Y, col=coord.X)
@property
def screen_size(self) -> WindowsCoordinates:
@ -390,9 +391,7 @@ class LegacyWindowsTerm:
WindowsCoordinates: The width and height of the screen as WindowsCoordinates.
"""
screen_size: COORD = GetConsoleScreenBufferInfo(self._handle).dwSize
return WindowsCoordinates(
row=cast(int, screen_size.Y), col=cast(int, screen_size.X)
)
return WindowsCoordinates(row=screen_size.Y, col=screen_size.X)
def write_text(self, text: str) -> None:
"""Write text directly to the terminal without any modification of styles

View File

@ -240,6 +240,7 @@ class VerticalCenter(JupyterMixin):
Args:
renderable (RenderableType): A renderable object.
style (StyleType, optional): An optional style to apply to the background. Defaults to None.
"""
def __init__(

View File

@ -9,6 +9,7 @@ from .text import Text
re_ansi = re.compile(
r"""
(?:\x1b[0-?])|
(?:\x1b\](.*?)\x1b\\)|
(?:\x1b([(@-Z\\-_]|\[[0-?]*[ -/]*[@-~]))
""",

View File

@ -1,13 +1,33 @@
from __future__ import annotations
import re
from functools import lru_cache
from typing import Callable
from ._cell_widths import CELL_WIDTHS
# Regex to match sequence of the most common character ranges
_is_single_cell_widths = re.compile("^[\u0020-\u006f\u00a0\u02ff\u0370-\u0482]*$").match
# Ranges of unicode ordinals that produce a 1-cell wide character
# This is non-exhaustive, but covers most common Western characters
_SINGLE_CELL_UNICODE_RANGES: list[tuple[int, int]] = [
(0x20, 0x7E), # Latin (excluding non-printable)
(0xA0, 0xAC),
(0xAE, 0x002FF),
(0x00370, 0x00482), # Greek / Cyrillic
(0x02500, 0x025FC), # Box drawing, box elements, geometric shapes
(0x02800, 0x028FF), # Braille
]
# A set of characters that are a single cell wide
_SINGLE_CELLS = frozenset(
[
character
for _start, _end in _SINGLE_CELL_UNICODE_RANGES
for character in map(chr, range(_start, _end + 1))
]
)
# When called with a string this will return True if all
# characters are single-cell, otherwise False
_is_single_cell_widths: Callable[[str], bool] = _SINGLE_CELLS.issuperset
@lru_cache(4096)
@ -23,9 +43,9 @@ def cached_cell_len(text: str) -> int:
Returns:
int: Get the number of cells required to display text.
"""
_get_size = get_character_cell_size
total_size = sum(_get_size(character) for character in text)
return total_size
if _is_single_cell_widths(text):
return len(text)
return sum(map(get_character_cell_size, text))
def cell_len(text: str, _cell_len: Callable[[str], int] = cached_cell_len) -> int:
@ -39,9 +59,9 @@ def cell_len(text: str, _cell_len: Callable[[str], int] = cached_cell_len) -> in
"""
if len(text) < 512:
return _cell_len(text)
_get_size = get_character_cell_size
total_size = sum(_get_size(character) for character in text)
return total_size
if _is_single_cell_widths(text):
return len(text)
return sum(map(get_character_cell_size, text))
@lru_cache(maxsize=4096)
@ -54,20 +74,7 @@ def get_character_cell_size(character: str) -> int:
Returns:
int: Number of cells (0, 1 or 2) occupied by that character.
"""
return _get_codepoint_cell_size(ord(character))
@lru_cache(maxsize=4096)
def _get_codepoint_cell_size(codepoint: int) -> int:
"""Get the cell size of a character.
Args:
codepoint (int): Codepoint of a character.
Returns:
int: Number of cells (0, 1 or 2) occupied by that character.
"""
codepoint = ord(character)
_table = CELL_WIDTHS
lower_bound = 0
upper_bound = len(_table) - 1

View File

@ -1,5 +1,5 @@
import platform
import re
import sys
from colorsys import rgb_to_hls
from enum import IntEnum
from functools import lru_cache
@ -15,7 +15,7 @@ if TYPE_CHECKING: # pragma: no cover
from .text import Text
WINDOWS = platform.system() == "Windows"
WINDOWS = sys.platform == "win32"
class ColorSystem(IntEnum):

View File

@ -1,6 +1,5 @@
import inspect
import os
import platform
import sys
import threading
import zlib
@ -76,7 +75,7 @@ if TYPE_CHECKING:
JUPYTER_DEFAULT_COLUMNS = 115
JUPYTER_DEFAULT_LINES = 100
WINDOWS = platform.system() == "Windows"
WINDOWS = sys.platform == "win32"
HighlighterType = Callable[[Union[str, "Text"]], "Text"]
JustifyMethod = Literal["default", "left", "center", "right", "full"]
@ -90,15 +89,15 @@ class NoChange:
NO_CHANGE = NoChange()
try:
_STDIN_FILENO = sys.__stdin__.fileno()
_STDIN_FILENO = sys.__stdin__.fileno() # type: ignore[union-attr]
except Exception:
_STDIN_FILENO = 0
try:
_STDOUT_FILENO = sys.__stdout__.fileno()
_STDOUT_FILENO = sys.__stdout__.fileno() # type: ignore[union-attr]
except Exception:
_STDOUT_FILENO = 1
try:
_STDERR_FILENO = sys.__stderr__.fileno()
_STDERR_FILENO = sys.__stderr__.fileno() # type: ignore[union-attr]
except Exception:
_STDERR_FILENO = 2
@ -1006,19 +1005,14 @@ class Console:
width: Optional[int] = None
height: Optional[int] = None
if WINDOWS: # pragma: no cover
streams = _STD_STREAMS_OUTPUT if WINDOWS else _STD_STREAMS
for file_descriptor in streams:
try:
width, height = os.get_terminal_size()
width, height = os.get_terminal_size(file_descriptor)
except (AttributeError, ValueError, OSError): # Probably not a terminal
pass
else:
for file_descriptor in _STD_STREAMS:
try:
width, height = os.get_terminal_size(file_descriptor)
except (AttributeError, ValueError, OSError):
pass
else:
break
else:
break
columns = self._environ.get("COLUMNS")
if columns is not None and columns.isdigit():
@ -1309,7 +1303,7 @@ class Console:
renderable = rich_cast(renderable)
if hasattr(renderable, "__rich_console__") and not isclass(renderable):
render_iterable = renderable.__rich_console__(self, _options) # type: ignore[union-attr]
render_iterable = renderable.__rich_console__(self, _options)
elif isinstance(renderable, str):
text_renderable = self.render_str(
renderable, highlight=_options.highlight, markup=_options.markup
@ -1386,9 +1380,14 @@ class Console:
extra_lines = render_options.height - len(lines)
if extra_lines > 0:
pad_line = [
[Segment(" " * render_options.max_width, style), Segment("\n")]
if new_lines
else [Segment(" " * render_options.max_width, style)]
(
[
Segment(" " * render_options.max_width, style),
Segment("\n"),
]
if new_lines
else [Segment(" " * render_options.max_width, style)]
)
]
lines.extend(pad_line * extra_lines)
@ -1437,9 +1436,11 @@ class Console:
rich_text.overflow = overflow
else:
rich_text = Text(
_emoji_replace(text, default_variant=self._emoji_variant)
if emoji_enabled
else text,
(
_emoji_replace(text, default_variant=self._emoji_variant)
if emoji_enabled
else text
),
justify=justify,
overflow=overflow,
style=style,
@ -1536,7 +1537,11 @@ class Console:
if isinstance(renderable, str):
append_text(
self.render_str(
renderable, emoji=emoji, markup=markup, highlighter=_highlighter
renderable,
emoji=emoji,
markup=markup,
highlight=highlight,
highlighter=_highlighter,
)
)
elif isinstance(renderable, Text):
@ -1986,6 +1991,20 @@ class Console:
):
buffer_extend(line)
def on_broken_pipe(self) -> None:
"""This function is called when a `BrokenPipeError` is raised.
This can occur when piping Textual output in Linux and macOS.
The default implementation is to exit the app, but you could implement
this method in a subclass to change the behavior.
See https://docs.python.org/3/library/signal.html#note-on-sigpipe for details.
"""
self.quiet = True
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
raise SystemExit(1)
def _check_buffer(self) -> None:
"""Check if the buffer may be rendered. Render it if it can (e.g. Console.quiet is False)
Rendering is supported on Windows, Unix and Jupyter environments. For
@ -1995,8 +2014,17 @@ class Console:
if self.quiet:
del self._buffer[:]
return
try:
self._write_buffer()
except BrokenPipeError:
self.on_broken_pipe()
def _write_buffer(self) -> None:
"""Write the buffer to the output file."""
with self._lock:
if self.record:
if self.record and not self._buffer_index:
with self._record_buffer_lock:
self._record_buffer.extend(self._buffer[:])
@ -2166,7 +2194,7 @@ class Console:
"""
text = self.export_text(clear=clear, styles=styles)
with open(path, "wt", encoding="utf-8") as write_file:
with open(path, "w", encoding="utf-8") as write_file:
write_file.write(text)
def export_html(
@ -2272,7 +2300,7 @@ class Console:
code_format=code_format,
inline_styles=inline_styles,
)
with open(path, "wt", encoding="utf-8") as write_file:
with open(path, "w", encoding="utf-8") as write_file:
write_file.write(html)
def export_svg(
@ -2561,7 +2589,7 @@ class Console:
font_aspect_ratio=font_aspect_ratio,
unique_id=unique_id,
)
with open(path, "wt", encoding="utf-8") as write_file:
with open(path, "w", encoding="utf-8") as write_file:
write_file.write(svg)

View File

@ -54,7 +54,7 @@ DEFAULT_STYLES: Dict[str, Style] = {
"logging.level.notset": Style(dim=True),
"logging.level.debug": Style(color="green"),
"logging.level.info": Style(color="blue"),
"logging.level.warning": Style(color="red"),
"logging.level.warning": Style(color="yellow"),
"logging.level.error": Style(color="red", bold=True),
"logging.level.critical": Style(color="red", bold=True, reverse=True),
"log.level": Style.null(),
@ -120,6 +120,7 @@ DEFAULT_STYLES: Dict[str, Style] = {
"traceback.exc_type": Style(color="bright_red", bold=True),
"traceback.exc_value": Style.null(),
"traceback.offset": Style(color="bright_red", bold=True),
"traceback.error_range": Style(underline=True, bold=True, dim=False),
"bar.back": Style(color="grey23"),
"bar.complete": Style(color="rgb(249,38,114)"),
"bar.finished": Style(color="rgb(114,156,31)"),

View File

@ -1,4 +1,3 @@
# coding: utf-8
"""Functions for reporting filesizes. Borrowed from https://github.com/PyFilesystem/pyfilesystem2
The functions declared in this module should cover the different
@ -27,7 +26,7 @@ def _to_str(
if size == 1:
return "1 byte"
elif size < base:
return "{:,} bytes".format(size)
return f"{size:,} bytes"
for i, suffix in enumerate(suffixes, 2): # noqa: B007
unit = base**i

View File

@ -98,7 +98,7 @@ class ReprHighlighter(RegexHighlighter):
r"(?P<number>(?<!\w)\-?[0-9]+\.?[0-9]*(e[-+]?\d+?)?\b|0x[0-9a-fA-F]*)",
r"(?P<path>\B(/[-\w._+]+)*\/)(?P<filename>[-\w._+]*)?",
r"(?<![\\\w])(?P<str>b?'''.*?(?<!\\)'''|b?'.*?(?<!\\)'|b?\"\"\".*?(?<!\\)\"\"\"|b?\".*?(?<!\\)\")",
r"(?P<url>(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#~]*)",
r"(?P<url>(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#~@]*)",
),
]

View File

@ -37,7 +37,7 @@ class Live(JupyterMixin, RenderHook):
Args:
renderable (RenderableType, optional): The renderable to live display. Defaults to displaying nothing.
console (Console, optional): Optional Console instance. Default will an internal Console instance writing to stdout.
console (Console, optional): Optional Console instance. Defaults to an internal Console instance writing to stdout.
screen (bool, optional): Enable alternate screen mode. Defaults to False.
auto_refresh (bool, optional): Enable auto refresh. If disabled, you will need to call `refresh()` or `update()` with refresh flag. Defaults to True
refresh_per_second (float, optional): Number of times per second to refresh the live display. Defaults to 4.

View File

@ -36,11 +36,13 @@ class RichHandler(Handler):
markup (bool, optional): Enable console markup in log messages. Defaults to False.
rich_tracebacks (bool, optional): Enable rich tracebacks with syntax highlighting and formatting. Defaults to False.
tracebacks_width (Optional[int], optional): Number of characters used to render tracebacks, or None for full width. Defaults to None.
tracebacks_code_width (int, optional): Number of code characters used to render tracebacks, or None for full width. Defaults to 88.
tracebacks_extra_lines (int, optional): Additional lines of code to render tracebacks, or None for full width. Defaults to None.
tracebacks_theme (str, optional): Override pygments theme used in traceback.
tracebacks_word_wrap (bool, optional): Enable word wrapping of long tracebacks lines. Defaults to True.
tracebacks_show_locals (bool, optional): Enable display of locals in tracebacks. Defaults to False.
tracebacks_suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
tracebacks_max_frames (int, optional): Optional maximum number of frames returned by traceback.
locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
Defaults to 10.
locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
@ -74,11 +76,13 @@ class RichHandler(Handler):
markup: bool = False,
rich_tracebacks: bool = False,
tracebacks_width: Optional[int] = None,
tracebacks_code_width: int = 88,
tracebacks_extra_lines: int = 3,
tracebacks_theme: Optional[str] = None,
tracebacks_word_wrap: bool = True,
tracebacks_show_locals: bool = False,
tracebacks_suppress: Iterable[Union[str, ModuleType]] = (),
tracebacks_max_frames: int = 100,
locals_max_length: int = 10,
locals_max_string: int = 80,
log_time_format: Union[str, FormatTimeCallable] = "[%x %X]",
@ -104,6 +108,8 @@ class RichHandler(Handler):
self.tracebacks_word_wrap = tracebacks_word_wrap
self.tracebacks_show_locals = tracebacks_show_locals
self.tracebacks_suppress = tracebacks_suppress
self.tracebacks_max_frames = tracebacks_max_frames
self.tracebacks_code_width = tracebacks_code_width
self.locals_max_length = locals_max_length
self.locals_max_string = locals_max_string
self.keywords = keywords
@ -140,6 +146,7 @@ class RichHandler(Handler):
exc_value,
exc_traceback,
width=self.tracebacks_width,
code_width=self.tracebacks_code_width,
extra_lines=self.tracebacks_extra_lines,
theme=self.tracebacks_theme,
word_wrap=self.tracebacks_word_wrap,
@ -147,6 +154,7 @@ class RichHandler(Handler):
locals_max_length=self.locals_max_length,
locals_max_string=self.locals_max_string,
suppress=self.tracebacks_suppress,
max_frames=self.tracebacks_max_frames,
)
message = record.getMessage()
if self.formatter:

View File

@ -1,4 +1,4 @@
from typing import cast, List, Optional, Tuple, TYPE_CHECKING, Union
from typing import TYPE_CHECKING, List, Optional, Tuple, Union
if TYPE_CHECKING:
from .console import (
@ -7,11 +7,11 @@ if TYPE_CHECKING:
RenderableType,
RenderResult,
)
from .jupyter import JupyterMixin
from .measure import Measurement
from .style import Style
from .segment import Segment
from .style import Style
PaddingDimensions = Union[int, Tuple[int], Tuple[int, int], Tuple[int, int, int, int]]
@ -66,10 +66,10 @@ class Padding(JupyterMixin):
_pad = pad[0]
return (_pad, _pad, _pad, _pad)
if len(pad) == 2:
pad_top, pad_right = cast(Tuple[int, int], pad)
pad_top, pad_right = pad
return (pad_top, pad_right, pad_top, pad_right)
if len(pad) == 4:
top, right, bottom, left = cast(Tuple[int, int, int, int], pad)
top, right, bottom, left = pad
return (top, right, bottom, left)
raise ValueError(f"1, 2 or 4 integers required for padding; {len(pad)} given")

View File

@ -22,11 +22,13 @@ class Panel(JupyterMixin):
Args:
renderable (RenderableType): A console renderable object.
box (Box, optional): A Box instance that defines the look of the border (see :ref:`appendix_box`.
Defaults to box.ROUNDED.
box (Box, optional): A Box instance that defines the look of the border (see :ref:`appendix_box`. Defaults to box.ROUNDED.
title (Optional[TextType], optional): Optional title displayed in panel header. Defaults to None.
title_align (AlignMethod, optional): Alignment of title. Defaults to "center".
subtitle (Optional[TextType], optional): Optional subtitle displayed in panel footer. Defaults to None.
subtitle_align (AlignMethod, optional): Alignment of subtitle. Defaults to "center".
safe_box (bool, optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.
expand (bool, optional): If True the panel will stretch to fill the console
width, otherwise it will be sized to fit the contents. Defaults to True.
expand (bool, optional): If True the panel will stretch to fill the console width, otherwise it will be sized to fit the contents. Defaults to True.
style (str, optional): The style of the panel (border and contents). Defaults to "none".
border_style (str, optional): The style of the border. Defaults to "none".
width (Optional[int], optional): Optional width of panel. Defaults to None to auto-detect.
@ -144,7 +146,8 @@ class Panel(JupyterMixin):
Padding(self.renderable, _padding) if any(_padding) else self.renderable
)
style = console.get_style(self.style)
border_style = style + console.get_style(self.border_style)
partial_border_style = console.get_style(self.border_style)
border_style = style + partial_border_style
width = (
options.max_width
if self.width is None
@ -172,6 +175,9 @@ class Panel(JupyterMixin):
text = text.copy()
text.truncate(width)
excess_space = width - cell_len(text.plain)
if text.style:
text.stylize(console.get_style(text.style))
if excess_space:
if align == "left":
return Text.assemble(
@ -200,7 +206,7 @@ class Panel(JupyterMixin):
title_text = self._title
if title_text is not None:
title_text.stylize_before(border_style)
title_text.stylize_before(partial_border_style)
child_width = (
width - 2
@ -249,7 +255,7 @@ class Panel(JupyterMixin):
subtitle_text = self._subtitle
if subtitle_text is not None:
subtitle_text.stylize_before(border_style)
subtitle_text.stylize_before(partial_border_style)
if subtitle_text is None or width <= 4:
yield Segment(box.get_bottom([width - 2]), border_style)

View File

@ -3,6 +3,7 @@ import collections
import dataclasses
import inspect
import os
import reprlib
import sys
from array import array
from collections import Counter, UserDict, UserList, defaultdict, deque
@ -15,6 +16,7 @@ from typing import (
Any,
Callable,
DefaultDict,
Deque,
Dict,
Iterable,
List,
@ -77,7 +79,10 @@ def _is_dataclass_repr(obj: object) -> bool:
# Digging in to a lot of internals here
# Catching all exceptions in case something is missing on a non CPython implementation
try:
return obj.__repr__.__code__.co_filename == dataclasses.__file__
return obj.__repr__.__code__.co_filename in (
dataclasses.__file__,
reprlib.__file__,
)
except Exception: # pragma: no coverage
return False
@ -130,17 +135,19 @@ def _ipy_display_hook(
if _safe_isinstance(value, ConsoleRenderable):
console.line()
console.print(
value
if _safe_isinstance(value, RichRenderable)
else Pretty(
value,
overflow=overflow,
indent_guides=indent_guides,
max_length=max_length,
max_string=max_string,
max_depth=max_depth,
expand_all=expand_all,
margin=12,
(
value
if _safe_isinstance(value, RichRenderable)
else Pretty(
value,
overflow=overflow,
indent_guides=indent_guides,
max_length=max_length,
max_string=max_string,
max_depth=max_depth,
expand_all=expand_all,
margin=12,
)
),
crop=crop,
new_line_start=True,
@ -196,16 +203,18 @@ def install(
assert console is not None
builtins._ = None # type: ignore[attr-defined]
console.print(
value
if _safe_isinstance(value, RichRenderable)
else Pretty(
value,
overflow=overflow,
indent_guides=indent_guides,
max_length=max_length,
max_string=max_string,
max_depth=max_depth,
expand_all=expand_all,
(
value
if _safe_isinstance(value, RichRenderable)
else Pretty(
value,
overflow=overflow,
indent_guides=indent_guides,
max_length=max_length,
max_string=max_string,
max_depth=max_depth,
expand_all=expand_all,
)
),
crop=crop,
)
@ -353,6 +362,16 @@ def _get_braces_for_defaultdict(_object: DefaultDict[Any, Any]) -> Tuple[str, st
)
def _get_braces_for_deque(_object: Deque[Any]) -> Tuple[str, str, str]:
if _object.maxlen is None:
return ("deque([", "])", "deque()")
return (
"deque([",
f"], maxlen={_object.maxlen})",
f"deque(maxlen={_object.maxlen})",
)
def _get_braces_for_array(_object: "array[Any]") -> Tuple[str, str, str]:
return (f"array({_object.typecode!r}, [", "])", f"array({_object.typecode!r})")
@ -362,7 +381,7 @@ _BRACES: Dict[type, Callable[[Any], Tuple[str, str, str]]] = {
array: _get_braces_for_array,
defaultdict: _get_braces_for_defaultdict,
Counter: lambda _object: ("Counter({", "})", "Counter()"),
deque: lambda _object: ("deque([", "])", "deque()"),
deque: _get_braces_for_deque,
dict: lambda _object: ("{", "}", "{}"),
UserDict: lambda _object: ("{", "}", "{}"),
frozenset: lambda _object: ("frozenset({", "})", "frozenset()"),
@ -762,7 +781,9 @@ def traverse(
)
for last, field in loop_last(
field for field in fields(obj) if field.repr
field
for field in fields(obj)
if field.repr and hasattr(obj, field.name)
):
child_node = _traverse(getattr(obj, field.name), depth=depth + 1)
child_node.key_repr = field.name
@ -846,7 +867,7 @@ def traverse(
pop_visited(obj_id)
else:
node = Node(value_repr=to_repr(obj), last=root)
node.is_tuple = _safe_isinstance(obj, tuple)
node.is_tuple = type(obj) == tuple
node.is_namedtuple = _is_namedtuple(obj)
return node

View File

@ -39,6 +39,11 @@ if sys.version_info >= (3, 8):
else:
from pip._vendor.typing_extensions import Literal # pragma: no cover
if sys.version_info >= (3, 11):
from typing import Self
else:
from pip._vendor.typing_extensions import Self # pragma: no cover
from . import filesize, get_console
from .console import Console, Group, JustifyMethod, RenderableType
from .highlighter import Highlighter
@ -70,7 +75,7 @@ class _TrackThread(Thread):
self.done = Event()
self.completed = 0
super().__init__()
super().__init__(daemon=True)
def run(self) -> None:
task_id = self.task_id
@ -78,7 +83,7 @@ class _TrackThread(Thread):
update_period = self.update_period
last_completed = 0
wait = self.done.wait
while not wait(update_period):
while not wait(update_period) and self.progress.live.is_started:
completed = self.completed
if last_completed != completed:
advance(task_id, completed - last_completed)
@ -104,6 +109,7 @@ def track(
sequence: Union[Sequence[ProgressType], Iterable[ProgressType]],
description: str = "Working...",
total: Optional[float] = None,
completed: int = 0,
auto_refresh: bool = True,
console: Optional[Console] = None,
transient: bool = False,
@ -123,6 +129,7 @@ def track(
sequence (Iterable[ProgressType]): A sequence (must support "len") you wish to iterate over.
description (str, optional): Description of task show next to progress bar. Defaults to "Working".
total: (float, optional): Total number of steps. Default is len(sequence).
completed (int, optional): Number of steps completed so far. Defaults to 0.
auto_refresh (bool, optional): Automatic refresh, disable to force a refresh after each iteration. Default is True.
transient: (bool, optional): Clear the progress on exit. Defaults to False.
console (Console, optional): Console to write to. Default creates internal Console instance.
@ -166,7 +173,11 @@ def track(
with progress:
yield from progress.track(
sequence, total=total, description=description, update_period=update_period
sequence,
total=total,
completed=completed,
description=description,
update_period=update_period,
)
@ -269,6 +280,9 @@ class _Reader(RawIOBase, BinaryIO):
def write(self, s: Any) -> int:
raise UnsupportedOperation("write")
def writelines(self, lines: Iterable[Any]) -> None:
raise UnsupportedOperation("writelines")
class _ReadContext(ContextManager[_I], Generic[_I]):
"""A utility class to handle a context for both a reader and a progress."""
@ -1050,7 +1064,7 @@ class Progress(JupyterMixin):
"""Renders an auto-updating progress bar(s).
Args:
console (Console, optional): Optional Console instance. Default will an internal Console instance writing to stdout.
console (Console, optional): Optional Console instance. Defaults to an internal Console instance writing to stdout.
auto_refresh (bool, optional): Enable auto refresh. If disabled, you will need to call `refresh()`.
refresh_per_second (Optional[float], optional): Number of times per second to refresh the progress information or None to use default (10). Defaults to None.
speed_estimate_period: (float, optional): Period (in seconds) used to calculate the speed estimate. Defaults to 30.
@ -1161,10 +1175,10 @@ class Progress(JupyterMixin):
def stop(self) -> None:
"""Stop the progress display."""
self.live.stop()
if not self.console.is_interactive:
if not self.console.is_interactive and not self.console.is_jupyter:
self.console.print()
def __enter__(self) -> "Progress":
def __enter__(self) -> Self:
self.start()
return self
@ -1180,6 +1194,7 @@ class Progress(JupyterMixin):
self,
sequence: Union[Iterable[ProgressType], Sequence[ProgressType]],
total: Optional[float] = None,
completed: int = 0,
task_id: Optional[TaskID] = None,
description: str = "Working...",
update_period: float = 0.1,
@ -1189,6 +1204,7 @@ class Progress(JupyterMixin):
Args:
sequence (Sequence[ProgressType]): A sequence of values you want to iterate over and track progress.
total: (float, optional): Total number of steps. Default is len(sequence).
completed (int, optional): Number of steps completed so far. Defaults to 0.
task_id: (TaskID): Task to track. Default is new task.
description: (str, optional): Description of task, if new task is created.
update_period (float, optional): Minimum time (in seconds) between calls to update(). Defaults to 0.1.
@ -1200,9 +1216,9 @@ class Progress(JupyterMixin):
total = float(length_hint(sequence)) or None
if task_id is None:
task_id = self.add_task(description, total=total)
task_id = self.add_task(description, total=total, completed=completed)
else:
self.update(task_id, total=total)
self.update(task_id, total=total, completed=completed)
if self.live.auto_refresh:
with _TrackThread(self, task_id, update_period) as track_thread:
@ -1326,7 +1342,7 @@ class Progress(JupyterMixin):
# normalize the mode (always rb, rt)
_mode = "".join(sorted(mode, reverse=False))
if _mode not in ("br", "rt", "r"):
raise ValueError("invalid mode {!r}".format(mode))
raise ValueError(f"invalid mode {mode!r}")
# patch buffering to provide the same behaviour as the builtin `open`
line_buffering = buffering == 1

View File

@ -108,7 +108,7 @@ class ProgressBar(JupyterMixin):
for index in range(PULSE_SIZE):
position = index / PULSE_SIZE
fade = 0.5 + cos((position * pi * 2)) / 2.0
fade = 0.5 + cos(position * pi * 2) / 2.0
color = blend_rgb(fore_color, back_color, cross_fade=fade)
append(_Segment(bar, _Style(color=from_triplet(color))))
return segments

View File

@ -36,6 +36,7 @@ class PromptBase(Generic[PromptType]):
console (Console, optional): A Console instance or None to use global console. Defaults to None.
password (bool, optional): Enable password input. Defaults to False.
choices (List[str], optional): A list of valid choices. Defaults to None.
case_sensitive (bool, optional): Matching of choices should be case-sensitive. Defaults to True.
show_default (bool, optional): Show default in prompt. Defaults to True.
show_choices (bool, optional): Show choices in prompt. Defaults to True.
"""
@ -57,6 +58,7 @@ class PromptBase(Generic[PromptType]):
console: Optional[Console] = None,
password: bool = False,
choices: Optional[List[str]] = None,
case_sensitive: bool = True,
show_default: bool = True,
show_choices: bool = True,
) -> None:
@ -69,6 +71,7 @@ class PromptBase(Generic[PromptType]):
self.password = password
if choices is not None:
self.choices = choices
self.case_sensitive = case_sensitive
self.show_default = show_default
self.show_choices = show_choices
@ -81,6 +84,7 @@ class PromptBase(Generic[PromptType]):
console: Optional[Console] = None,
password: bool = False,
choices: Optional[List[str]] = None,
case_sensitive: bool = True,
show_default: bool = True,
show_choices: bool = True,
default: DefaultType,
@ -97,6 +101,7 @@ class PromptBase(Generic[PromptType]):
console: Optional[Console] = None,
password: bool = False,
choices: Optional[List[str]] = None,
case_sensitive: bool = True,
show_default: bool = True,
show_choices: bool = True,
stream: Optional[TextIO] = None,
@ -111,6 +116,7 @@ class PromptBase(Generic[PromptType]):
console: Optional[Console] = None,
password: bool = False,
choices: Optional[List[str]] = None,
case_sensitive: bool = True,
show_default: bool = True,
show_choices: bool = True,
default: Any = ...,
@ -126,6 +132,7 @@ class PromptBase(Generic[PromptType]):
console (Console, optional): A Console instance or None to use global console. Defaults to None.
password (bool, optional): Enable password input. Defaults to False.
choices (List[str], optional): A list of valid choices. Defaults to None.
case_sensitive (bool, optional): Matching of choices should be case-sensitive. Defaults to True.
show_default (bool, optional): Show default in prompt. Defaults to True.
show_choices (bool, optional): Show choices in prompt. Defaults to True.
stream (TextIO, optional): Optional text file open for reading to get input. Defaults to None.
@ -135,6 +142,7 @@ class PromptBase(Generic[PromptType]):
console=console,
password=password,
choices=choices,
case_sensitive=case_sensitive,
show_default=show_default,
show_choices=show_choices,
)
@ -212,7 +220,9 @@ class PromptBase(Generic[PromptType]):
bool: True if choice was valid, otherwise False.
"""
assert self.choices is not None
return value.strip() in self.choices
if self.case_sensitive:
return value.strip() in self.choices
return value.strip().lower() in [choice.lower() for choice in self.choices]
def process_response(self, value: str) -> PromptType:
"""Process response from user, convert to prompt type.
@ -232,9 +242,17 @@ class PromptBase(Generic[PromptType]):
except ValueError:
raise InvalidResponse(self.validate_error_message)
if self.choices is not None and not self.check_choice(value):
raise InvalidResponse(self.illegal_choice_message)
if self.choices is not None:
if not self.check_choice(value):
raise InvalidResponse(self.illegal_choice_message)
if not self.case_sensitive:
# return the original choice, not the lower case version
return_value = self.response_type(
self.choices[
[choice.lower() for choice in self.choices].index(value.lower())
]
)
return return_value
def on_validate_error(self, value: str, error: InvalidResponse) -> None:
@ -371,5 +389,12 @@ if __name__ == "__main__": # pragma: no cover
fruit = Prompt.ask("Enter a fruit", choices=["apple", "orange", "pear"])
print(f"fruit={fruit!r}")
doggie = Prompt.ask(
"What's the best Dog? (Case INSENSITIVE)",
choices=["Border Terrier", "Collie", "Labradoodle"],
case_sensitive=False,
)
print(f"doggie={doggie!r}")
else:
print("[b]OK :loudly_crying_face:")

Some files were not shown because too many files have changed in this diff Show More