ขยายเส้นทางการค้นหา Python ไปยังแหล่งที่มาอื่น


110

ฉันเพิ่งเข้าร่วมโครงการที่มีฐานรหัสที่มีอยู่ค่อนข้างใหญ่ เราพัฒนาใน linux และไม่ใช้และ IDE เราทำงานผ่านบรรทัดคำสั่ง ฉันกำลังพยายามหาวิธีทำให้ python ค้นหาเส้นทางที่ถูกต้องเมื่อฉันเรียกใช้โมดูลโครงการ ตัวอย่างเช่นเมื่อฉันเรียกใช้สิ่งต่างๆเช่น:

python someprojectfile.py

ฉันเข้าใจ

ImportError: no module named core.'somemodule'

ฉันได้รับสิ่งนี้สำหรับการนำเข้าทั้งหมดของฉันโดยถือว่าเป็นปัญหากับเส้นทาง

TLDR:

ฉันจะให้ Python ค้นหา~/codez/project/และไฟล์และโฟลเดอร์ทั้งหมดสำหรับไฟล์ * .py ระหว่างคำสั่งนำเข้าได้อย่างไร

คำตอบ:


174

มีหลายวิธีที่เป็นไปได้ในการดำเนินการนี้:

  • ตั้งค่าตัวแปรสภาพแวดล้อมPYTHONPATHเป็นรายการไดเร็กทอรีที่คั่นด้วยโคลอนเพื่อค้นหาโมดูลที่อิมพอร์ต
  • ในโปรแกรมของคุณใช้sys.path.append('/path/to/search')เพื่อเพิ่มชื่อไดเร็กทอรีที่คุณต้องการให้ Python ค้นหาโมดูลที่นำเข้า sys.pathเป็นเพียงรายการไดเรกทอรีที่ Python ค้นหาทุกครั้งที่ถูกขอให้นำเข้าโมดูลและคุณสามารถแก้ไขได้ตามต้องการ (แม้ว่าฉันจะไม่แนะนำให้ลบไดเรกทอรีมาตรฐานใด ๆ ก็ตาม!) ไดเรกทอรีใด ๆ ที่คุณใส่ในตัวแปรสภาพแวดล้อมPYTHONPATHจะถูกแทรกลงในsys.pathเมื่อ Python เริ่มทำงาน
  • ใช้เพื่อเพิ่มไดเรกทอรีที่site.addsitedir sys.pathความแตกต่างระหว่างสิ่งนี้และการต่อท้ายแบบธรรมดาก็คือเมื่อคุณใช้addsitedirมันยังค้นหา.pthไฟล์ภายในไดเร็กทอรีนั้นและใช้เพื่อเพิ่มไดเร็กทอรีเพิ่มเติมsys.pathตามเนื้อหาของไฟล์ ดูเอกสารสำหรับรายละเอียดเพิ่มเติม

คุณต้องการใช้อันไหนขึ้นอยู่กับสถานการณ์ของคุณ โปรดจำไว้ว่าเมื่อคุณแจกจ่ายโปรเจ็กต์ของคุณไปยังผู้ใช้รายอื่นพวกเขามักจะติดตั้งในลักษณะที่ผู้นำเข้าของ Python ตรวจพบไฟล์โค้ด Python โดยอัตโนมัติ (เช่นมักจะติดตั้งแพ็คเกจในsite-packagesไดเร็กทอรี) ดังนั้นหากคุณยุ่งกับsys.pathโค้ดของคุณ ซึ่งอาจไม่จำเป็นและอาจส่งผลเสียด้วยซ้ำเมื่อรหัสนั้นทำงานบนคอมพิวเตอร์เครื่องอื่น สำหรับการพัฒนาฉันจะเดาว่าการตั้งค่าPYTHONPATHมักจะเป็นวิธีที่ดีที่สุด

อย่างไรก็ตามเมื่อคุณใช้งานบางอย่างที่ทำงานบนคอมพิวเตอร์ของคุณเอง (หรือเมื่อคุณมีการตั้งค่าที่ไม่เป็นมาตรฐานเช่นบางครั้งในเฟรมเวิร์กของเว็บแอป) ไม่ใช่เรื่องแปลกที่จะทำบางสิ่งเช่น

import sys
from os.path import dirname
sys.path.append(dirname(__file__))

ดังนั้นถ้าฉันต้องการบอกว่า 15 ไดเรกทอรีย่อยฉันจะต้องเพิ่มทีละรายการ?
themaestro

และคุณช่วยยกตัวอย่างอาร์กิวเมนต์บรรทัดคำสั่งเพื่อเปลี่ยน PYTHONPATH ได้ไหม
themaestro

3
ไปยังชุดPYTHONPATHใน.bashrcหรือสิ่งที่เริ่มต้นยื่นการใช้เปลือกของคุณ (หากยังไม่ได้ทุบตี) export PYTHONPATH=$PYTHONPATH:$HOME/codez/projectเขียน แต่ถ้าคุณมีพวงของไดเรกทอรีย่อยที่ผมต้องการให้ไฟล์และการใช้งาน.pth site.addsitedirคุณสามารถสร้างโมดูลsitecustomizeที่เรียกใช้ฟังก์ชันให้คุณได้ ลองวางไว้ที่~/.local/lib/python2.6/sitecustomize.py(แทนที่เวอร์ชัน Python ของคุณ) หวังว่าจะได้รับการนำเข้าโดยอัตโนมัติ
David Z

ฉันใส่สิ่งต่อไปนี้ในไฟล์. bashrc ของฉันและฉันยังไม่มีโชคกับการนำเข้าเหล่านั้น ความคิดใด ๆ ? ฉันจะสร้างไฟล์. pth ได้อย่างไร ส่งออก PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / ส่งออก PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / project export PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / project / core export PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / project / core export PYTHONPATH: $ PYTHONPATH HOME / adaifotis / codez / project / proxies export PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / project / conf
themaestro

echo $PYTHONPATHลองเปิดสถานีและทำงาน หากตัวแปรสภาพแวดล้อมได้รับการตั้งค่าอย่างถูกต้องคุณควรเห็นรายการไดเร็กทอรีที่คั่นด้วยโคลอน สำหรับข้อมูลเกี่ยวกับ.pthไฟล์โปรดดูเอกสารสำหรับsiteโมดูลที่ฉันเชื่อมโยงไว้ในคำตอบของฉัน จะบอกคุณว่าเนื้อหาควรเป็นอย่างไรและใช้อย่างไร
David Z

13

นอกจากนี้คุณควรอ่านเกี่ยวกับแพคเกจหลามที่นี่: http://docs.python.org/tutorial/modules.html

~/codez/projectจากตัวอย่างของคุณผมเดาว่าคุณจริงๆมีแพคเกจที่ ไฟล์__init__.pyในไดเร็กทอรี python จะแมปไดเร็กทอรีกับเนมสเปซ หากไดเร็กทอรีย่อยของคุณมี__init__.pyไฟล์ทั้งหมดคุณจะต้องเพิ่มไดเร็กทอรีฐานในPYTHONPATHไฟล์. ตัวอย่างเช่น:

PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / project

นอกเหนือจากการทดสอบตัวแปรสภาพแวดล้อม PYTHONPATH ของคุณตามที่ David อธิบายแล้วคุณสามารถทดสอบใน python ดังนี้:

$ python
>>> import project                      # should work if PYTHONPATH set
>>> import sys
>>> for line in sys.path: print line    # print current python path

...


คุณอาจต้องการที่จะนำคุณ__init__.pyใน backticks จะทำให้มันชัดเจนคุณหมายความว่าจริงๆเมื่อเทียบกับ__init__.py init.pyเพียงเพื่อไม่ให้มือใหม่สับสน :)
antred

5

ฉันรู้ว่ากระทู้นี้เก่าไปหน่อย แต่ฉันต้องใช้เวลาพอสมควรในการทำความเข้าใจเรื่องนี้ฉันจึงอยากแบ่งปัน

ในโปรเจ็กต์ของฉันฉันมีสคริปต์หลักในไดเร็กทอรีหลักและเพื่อแยกความแตกต่างของโมดูลฉันใส่โมดูลที่สนับสนุนทั้งหมดไว้ในโฟลเดอร์ย่อยที่เรียกว่า "โมดูล" ในสคริปต์หลักของฉันฉันนำเข้าโมดูลเหล่านี้เช่นนี้ (สำหรับโมดูลที่เรียกว่า report.py):

from modules.report import report, reportError

ถ้าฉันเรียกสคริปต์หลักของฉันสิ่งนี้ได้ผล อย่างไรก็ตามฉันต้องการทดสอบแต่ละโมดูลโดยรวม a main()ในแต่ละโมดูลและเรียกแต่ละโมดูลโดยตรงว่า:

python modules/report.py

ตอนนี้ Python บ่นว่าไม่พบ "โมดูลที่เรียกว่าโมดูล" กุญแจสำคัญในที่นี้คือโดยค่าเริ่มต้น Python จะรวมโฟลเดอร์ของสคริปต์ไว้ในเส้นทางการค้นหา แต่ไม่ใช่ CWD ข้อผิดพลาดนี้บอกว่าจริงๆแล้วคือ "ฉันไม่พบโฟลเดอร์ย่อยของโมดูล" เนื่องจากไม่มีไดเร็กทอรีย่อย "module" จากไดเร็กทอรีที่มีโมดูล report.py อยู่

ฉันพบว่าวิธีแก้ปัญหาล่าสุดคือการผนวก CWD ในเส้นทางการค้นหา Python โดยรวมสิ่งนี้ไว้ที่ด้านบน:

import sys

sys.path.append(".")

ตอนนี้ Python ค้นหา CWD (ไดเร็กทอรีปัจจุบัน) พบโฟลเดอร์ย่อย "โมดูล" และทุกอย่างก็เรียบร้อยดี


3

ฉันอ่านคำถามนี้เพื่อหาคำตอบและไม่ชอบคำถามใด ๆ

ดังนั้นฉันจึงเขียนวิธีแก้ปัญหาอย่างรวดเร็วและสกปรก เพียงวางสิ่งนี้ไว้ที่ใดที่หนึ่งบน sys.path ของคุณและจะเพิ่มไดเร็กทอรีใด ๆ ภายใต้folder(จากไดเร็กทอรีการทำงานปัจจุบัน) หรือภายใต้abspath:

#using.py

import sys, os.path

def all_from(folder='', abspath=None):
    """add all dirs under `folder` to sys.path if any .py files are found.
    Use an abspath if you'd rather do it that way.

    Uses the current working directory as the location of using.py. 
    Keep in mind that os.walk goes *all the way* down the directory tree.
    With that, try not to use this on something too close to '/'

    """
    add = set(sys.path)
    if abspath is None:
        cwd = os.path.abspath(os.path.curdir)
        abspath = os.path.join(cwd, folder)
    for root, dirs, files in os.walk(abspath):
        for f in files:
            if f[-3:] in '.py':
                add.add(root)
                break
    for i in add: sys.path.append(i)

>>> import using, sys, pprint
>>> using.all_from('py') #if in ~, /home/user/py/
>>> pprint.pprint(sys.path)
[
#that was easy
]

และฉันชอบมันเพราะฉันสามารถมีโฟลเดอร์สำหรับเครื่องมือสุ่มบางอย่างและไม่ให้มันเป็นส่วนหนึ่งของแพ็คเกจหรืออะไรก็ได้และยังสามารถเข้าถึงบางส่วน (หรือทั้งหมด) ของพวกมันได้ในสองสามบรรทัดของโค้ด


3

วิธีที่ง่ายที่สุดที่ฉันพบคือสร้างไฟล์ "any_name.pth" และวางไว้ในโฟลเดอร์ "\ Lib \ site-Packages" คุณจะพบโฟลเดอร์นั้นทุกที่ที่ติดตั้ง python

ในไฟล์นั้นให้ใส่รายการไดเร็กทอรีที่คุณต้องการเก็บโมดูลสำหรับการนำเข้า ตัวอย่างเช่นสร้างบรรทัดในไฟล์ดังต่อไปนี้:

C: \ Users \ example ... \ example

คุณจะสามารถบอกได้ว่ามันใช้งานได้โดยเรียกใช้สิ่งนี้ใน python:

import sys
for line in sys: print line

คุณจะเห็นไดเร็กทอรีของคุณพิมพ์ออกมารวมถึงไดเร็กทอรีอื่น ๆ ที่คุณสามารถนำเข้าได้ ตอนนี้คุณสามารถนำเข้าไฟล์ "mymodule.py" ที่อยู่ในไดเร็กทอรีนั้นได้ง่ายๆดังนี้:

import mymodule

สิ่งนี้จะไม่นำเข้าโฟลเดอร์ย่อย สำหรับสิ่งนั้นคุณสามารถจินตนาการถึงการสร้างสคริปต์ python เพื่อสร้างไฟล์. pth ที่มีโฟลเดอร์ย่อยทั้งหมดของโฟลเดอร์ที่คุณกำหนด ให้รันเมื่อเริ่มต้นใช้งาน


0

ตัวเลือกใหม่สำหรับคำถามเก่า
การติดตั้งfail2banแพคเกจใน Debian, ดูเหมือนว่ามัน hardcoded จะติดตั้งบน/usr/lib/python3/dist-packages/fail2banเส้นทางที่ไม่ได้อยู่ใน sys.pathpython3


> python3
Python 3.7.3 (v3.7.3:ef4ec6ed12, Jun 25 2019, 18:51:50)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib/python37.zip', '/usr/lib/python3.7', '/usr/lib/python3.7/lib-dynload', '/usr/lib/python3.7/site-packages']
>>>

ดังนั้นแทนที่จะแค่คัดลอกฉัน (ทุบตี) เชื่อมโยงไลบรารีกับเวอร์ชันที่ใหม่กว่า
การอัปเดตแอปดั้งเดิมในอนาคตจะถูกนำไปใช้กับเวอร์ชันที่เชื่อมโยงโดยอัตโนมัติ

 if [ -d /usr/lib/python3/dist-packages/fail2ban ]
   then
      for d in /usr/lib/python3.*
      do
         [ -d ${d}/fail2ban ] || \
            ln -vs /usr/lib/python3/dist-packages/fail2ban ${d}/
      done
   fi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.