Bagaimana perpustakaan lingkungan virtual bekerja

Pernahkah Anda berpikir tentang bagaimana perpustakaan lingkungan virtual bekerja di Python? Dalam artikel ini, saya mengusulkan untuk berkenalan dengan konsep utama yang digunakan semua perpustakaan untuk lingkungan, seperti virtualenv, virtualenvwrapper, conda, pipenv.

Awalnya, dalam Python tidak ada kemampuan bawaan untuk membuat lingkungan, dan fitur ini diimplementasikan sebagai peretasan. Ternyata, semua perpustakaan didasarkan pada fitur interpreter python yang sangat sederhana.

Ketika Python memulai interpreter, ia mulai mencari direktori dengan modul (paket-situs). Pencarian dimulai dengan direktori induk mengenai lokasi fisik dari interpreter executable (python.exe). Jika folder modul tidak ditemukan, maka Python naik satu tingkat lebih tinggi dan melakukannya hingga direktori root tercapai. Untuk memahami bahwa ini adalah direktori dengan modul, Python mencari modul os, yang harus ada dalam file os.py dan diperlukan agar python berfungsi.

Mari kita bayangkan bahwa juru bahasa kita terletak di /usr/dev/lang/bin/python . Maka jalur pencarian akan terlihat seperti ini:

 /usr/dev/lang/lib/python3.7/os.py /usr/dev/lib/python3.7/os.py /usr/lib/python3.7/os.py /lib/python3.7/os.py 

Seperti yang Anda lihat, Python menambahkan awalan khusus ( lib/python$VERSION/os.py ) ke jalur kami. Segera setelah penerjemah menemukan kecocokan pertama (keberadaan file os.py), ia mengubah sys.prefix dan sys.exec_prefix ke jalur ini (dengan awalan dihapus). Jika karena alasan tertentu tidak ada kecocokan yang ditemukan, maka jalur standar digunakan, yang dikompilasi ke dalam interpreter.

Sekarang mari kita lihat bagaimana salah satu perpustakaan tertua dan paling terkenal, virtualenv, melakukannya.

 user@arb:/usr/home/test# virtualenv ENV Running virtualenv with interpreter /usr/bin/python3 New python executable in /usr/home/test/ENV/bin/python3 Also creating executable in /usr/home/test/ENV/bin/python Installing setuptools, pkg_resources, pip, wheel...done. 

Setelah eksekusi, itu membuat direktori tambahan:

 user@arb:/usr/home/test/ENV# tree -L 3 . ├── bin │ ├── activate │ ├── activate.csh │ ├── activate.fish │ ├── activate_this.py │ ├── easy_install │ ├── easy_install-3.7 │ ├── pip │ ├── pip3 │ ├── pip3.7 │ ├── python │ ├── python-config │ ├── python3 -> python │ ├── python3.7 -> python │ └── wheel ├── include │ └── python3.7m -> /usr/include/python3.7m ├── lib │ └── python3.7 │ ├── __future__.py -> /usr/lib/python3.7/__future__.py │ ├── __pycache__ │ ├── _bootlocale.py -> /usr/lib/python3.7/_bootlocale.py │ ├── _collections_abc.py -> /usr/lib/python3.7/_collections_abc.py │ ├── _dummy_thread.py -> /usr/lib/python3.7/_dummy_thread.py │ ├── _weakrefset.py -> /usr/lib/python3.7/_weakrefset.py │ ├── abc.py -> /usr/lib/python3.7/abc.py │ ├── base64.py -> /usr/lib/python3.7/base64.py │ ├── bisect.py -> /usr/lib/python3.7/bisect.py │ ├── codecs.py -> /usr/lib/python3.7/codecs.py │ ├── collections -> /usr/lib/python3.7/collections │ ├── config-3.7m-darwin -> /usr/lib/python3.7/config-3.7m-darwin │ ├── copy.py -> /usr/lib/python3.7/copy.py │ ├── copyreg.py -> /usr/lib/python3.7/copyreg.py │ ├── distutils │ ├── encodings -> /usr/lib/python3.7/encodings │ ├── enum.py -> /usr/lib/python3.7/enum.py │ ├── fnmatch.py -> /usr/lib/python3.7/fnmatch.py │ ├── functools.py -> /usr/lib/python3.7/functools.py │ ├── genericpath.py -> /usr/lib/python3.7/genericpath.py │ ├── hashlib.py -> /usr/lib/python3.7/hashlib.py │ ├── heapq.py -> /usr/lib/python3.7/heapq.py │ ├── hmac.py -> /usr/lib/python3.7/hmac.py │ ├── imp.py -> /usr/lib/python3.7/imp.py │ ├── importlib -> /usr/lib/python3.7/importlib │ ├── io.py -> /usr/lib/python3.7/io.py │ ├── keyword.py -> /usr/lib/python3.7/keyword.py │ ├── lib-dynload -> /usr/lib/python3.7/lib-dynload │ ├── linecache.py -> /usr/lib/python3.7/linecache.py │ ├── locale.py -> /usr/lib/python3.7/locale.py │ ├── no-global-site-packages.txt │ ├── ntpath.py -> /usr/lib/python3.7/ntpath.py │ ├── operator.py -> /usr/lib/python3.7/operator.py │ ├── orig-prefix.txt │ ├── os.py -> /usr/lib/python3.7/os.py │ ├── posixpath.py -> /usr/lib/python3.7/posixpath.py │ ├── random.py -> /usr/lib/python3.7/random.py │ ├── re.py -> /usr/lib/python3.7/re.py │ ├── readline.so -> /usr/lib/python3.7/lib-dynload/readline.cpython-37m-darwin.so │ ├── reprlib.py -> /usr/lib/python3.7/reprlib.py │ ├── rlcompleter.py -> /usr/lib/python3.7/rlcompleter.py │ ├── shutil.py -> /usr/lib/python3.7/shutil.py │ ├── site-packages │ ├── site.py │ ├── sre_compile.py -> /usr/lib/python3.7/sre_compile.py │ ├── sre_constants.py -> /usr/lib/python3.7/sre_constants.py │ ├── sre_parse.py -> /usr/lib/python3.7/sre_parse.py │ ├── stat.py -> /usr/lib/python3.7/stat.py │ ├── struct.py -> /usr/lib/python3.7/struct.py │ ├── tarfile.py -> /usr/lib/python3.7/tarfile.py │ ├── tempfile.py -> /usr/lib/python3.7/tempfile.py │ ├── token.py -> /usr/lib/python3.7/token.py │ ├── tokenize.py -> /usr/lib/python3.7/tokenize.py │ ├── types.py -> /usr/lib/python3.7/types.py │ ├── warnings.py -> /usr/lib/python3.7/warnings.py │ └── weakref.py -> /usr/lib/python3.7/weakref.py └── pip-selfcheck.json 

Seperti yang Anda lihat, lingkungan virtual dibuat dengan menyalin biner Python ke folder lokal (ENV / bin / python). Kami juga dapat memperhatikan bahwa folder induk berisi tautan simbolis ke file pustaka standar python. Kami tidak dapat membuat tautan simbolis ke file yang dapat dieksekusi, karena penerjemah masih akan mengganti nama ke jalur yang sebenarnya.

Sekarang mari kita aktifkan lingkungan kita:

 user@arb:/usr/home/test# source ENV/bin/activate 

Perintah ini mengubah variabel lingkungan $ PATH sehingga perintah python menunjuk ke versi lokal kami dari python. Ini dicapai dengan mengganti lintasan lokal folder bin pada awal baris $ PATH sehingga lintasan lokal didahulukan dari semua lintasan di sebelah kanan.

 export "/usr/home/test/ENV/bin:$PATH" echo $PATH 

Jika Anda menjalankan skrip dari lingkungan ini, skrip akan dieksekusi menggunakan biner di /usr/home/test/ENV/bin/python . Penerjemah akan menggunakan jalur ini sebagai titik awal untuk menemukan modul. Dalam kasus kami, modul-modul perpustakaan standar akan ditemukan di path /usr/home/test/ENV/lib/python3.7/ .

Ini adalah retasan utama, berkat semua pustaka untuk bekerja dengan lingkungan virtual bekerja.

Perbaikan dalam Python 3


Dimulai dengan versi Python 3.3, standar baru telah muncul, disebut sebagai PEP 405 , yang memperkenalkan mekanisme baru untuk lingkungan yang ringan.

PEP ini menambahkan langkah ekstra untuk proses pencarian. Jika Anda membuat pyenv.cfg konfigurasi pyenv.cfg , maka alih-alih menyalin biner Python dan semua modulnya, Anda bisa dengan mudah menunjukkan lokasinya di konfigurasi ini.

Fitur ini secara aktif digunakan oleh modul venv standar, yang muncul dalam Python 3.

 user@arb:/usr/home/test2# python3 -m venv ENV user@arb:/usr/home/test2# tree -L 3 . └── ENV ├── bin │ ├── activate │ ├── activate.csh │ ├── activate.fish │ ├── easy_install │ ├── easy_install-3.7 │ ├── pip │ ├── pip3 │ ├── pip3.5 │ ├── python -> python3 │ └── python3 -> /usr/bin/python3 ├── include ├── lib │ └── python3.7 ├── lib64 -> lib ├── pyvenv.cfg └── share └── python-wheels 

 user@arb:/usr/home/test2# cat ENV/pyvenv.cfg home = /usr/bin include-system-site-packages = false version = 3.7.0 user@arb:/usr/home/test2# readlink ENV/bin/python3 /usr/bin/python3 

Berkat konfigurasi ini, alih-alih menyalin biner, venv hanya membuat tautan ke sana. Jika parameter include-system-site-packages diubah menjadi true , maka semua modul perpustakaan standar akan secara otomatis dapat diakses dari lingkungan virtual.

Terlepas dari perubahan ini, sebagian besar pustaka pihak ketiga untuk bekerja dengan lingkungan virtual menggunakan pendekatan lama.

PS: Saya penulis artikel ini, kamu bisa bertanya.

Source: https://habr.com/ru/post/id418579/


All Articles