<p>pip لا يدعم المسارات النسبية في مراجع URL المباشرة</p>

تم إنشاؤها على ٢٨ يونيو ٢٠١٩  ·  7تعليقات  ·  مصدر: pypa/pip

بيئة

  • إصدار النقطة: 19.1.1
  • إصدار Python: 3.7
  • نظام التشغيل: MacOS Mojave
لا تتردد في إضافة المزيد من المعلومات حول بيئتك هنا

وصف
لست متأكدًا مما إذا كان هذا خطأ في six أم خطأ Pip. إسمح لي إذا كان ينتمي إلى six .

يبدو أن النقطة تسمح بالمسارات المحلية في install_requires عبر name @ ./some/path ، لكن تحليل عنوان URL معطل بشكل رهيب.

https://github.com/pypa/pip/blob/a38a0eacd4650a7d9af4e6831a2c0aed0b6a0329/src/pip/_internal/download.py#L670 -L690

في هذه الوظيفة ، تستخدم urlsplit للحصول على المكونات الفردية لعنوان URL الوارد.

إليك ما يبدو عليه الأمر مع بعض أجزاء المدخلات:

./foo/bar -> SplitResult(scheme='', netloc='', path='./foo/bar', query='', fragment='')
file:./foo/bar -> SplitResult(scheme='file', netloc='', path='./foo/bar', query='', fragment='')
file://./foo/bar -> SplitResult(scheme='file', netloc='.', path='/foo/bar', query='', fragment='')

لاحظ أن النتيجة الأخيرة هي أن netloc هو . بدلاً من كونها فارغة و path مطلقة وليست محلية. يؤدي هذا إلى حل الخطأ فيما يتعلق بالمسارات غير المحلية. كل هذا جيد وجيد - يمكنني استخدام النموذج الثاني لإرضاء المنطق الشرطي (على الرغم من أنه يجب أن يدعم الأول أيضًا).

ومع ذلك ، هناك منطق متضارب في مكان آخر ...

https://github.com/pypa/pip/blob/169eebdb6e36a31e545804228cad94902a1ec8e9/src/pip/_vendor/packaging/requirements.py#L103 -L106

هذا هو المنطق الذي فشل على الرغم من أننا استوفينا المنطق السابق.

فيما يلي وظيفة اختبار توضح المشكلة:

from six.moves.urllib import parse as urllib_parse

def tryparse(url):
    print(url)
    parsed = urllib_parse.urlparse(url)
    unparsed = urllib_parse.urlunparse(parsed)
    parsed_again = urllib_parse.urlparse(unparsed)
    print(parsed)
    print(unparsed)
    print(parsed_again)

هذا هو ناتج ./foo/bar :

>>> tryparse('./foo/bar')
./foo/bar
ParseResult(scheme='', netloc='', path='./foo/bar', params='', query='', fragment='')
./foo/bar
ParseResult(scheme='', netloc='', path='./foo/bar', params='', query='', fragment='')

كل شيء جيد ، على الرغم من أنه لا يلبي منطق الوظيفة الأولى الذي يتطلب مخططًا file: .

هذا هو ناتج file:./foo/bar :

>>> tryparse('file:./foo/bar')
file:./foo/bar
ParseResult(scheme='file', netloc='', path='./foo/bar', params='', query='', fragment='')
file:///./foo/bar
ParseResult(scheme='file', netloc='', path='/./foo/bar', params='', query='', fragment='')

وجه الفتاة! لاحظ كيف ، عندما "نحلل" النتيجة من مكالمة التحليل الأولى ، يصبح path مطلقًا file:///... .

هذا هو سبب فشل الاختيار الثاني المذكور - المسار ليس محليًا. أعتقد أن هذا خطأ في six ولكن يمكن تخفيفه في Pip بالسماح scheme in ['file', ''] وتوجيه المستخدمين لاستخدام نموذج URI ./foo/bar .

بالنظر إلى هذين المنطقين المتناقضين ، من المستحيل استخدام المسارات المحلية في مفاتيح install_requires في تكوينات distutils أو setuptools .

سلوك متوقع
يجب أن أكون قادرًا على عمل name @ ./some/path (أو ، بصراحة ، ./some/path ) لتحديد حزمة موردة محلية لقاعدة الشفرة الخاصة بي.

كيفية التكاثر

#!/usr/bin/env bash
mkdir /tmp/pip-uri-repro && cd /tmp/pip-uri-repro

mkdir -p foo/bar

cat > requirements.txt <<EOF
./foo
EOF

cat > foo/setup.py <<EOF
#!/usr/bin/env python
from setuptools import setup
setup(
    name="foo",
    version="0.1",
    install_requires=[
        "bar @ file:./bar"
    ]
)
EOF

cat > foo/bar/setup.py <<EOF
#!/usr/bin/env python
from setuptools import setup
setup(
    name="bar",
    version="0.1"
)
EOF

# (OUTPUT 1)
pip install -r requirements.txt

cat > foo/setup.py <<EOF
#!/usr/bin/env python
from setuptools import setup
setup(
    name="foo",
    version="0.1",
    install_requires=[
        # we're forced to use an absolute path
        # to make the "Invalid URL" error go
        # away, which isn't right anyway (the
        # error that is raised as a result
        # is justified)
        "bar @ file://./bar"
    ]
)
EOF

# (OUTPUT 2)
pip install -r requirements.txt

انتاج |

من أول pip install :

Processing ./foo
    ERROR: Complete output from command python setup.py egg_info:
    ERROR: error in foo setup command: 'install_requires' must be a string or list of strings containing valid project/version requirement specifiers; Invalid URL given

من الثاني pip install :

Processing ./foo
ERROR: Exception:
Traceback (most recent call last):
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/cli/base_command.py", line 178, in main
    status = self.run(options, args)
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/commands/install.py", line 352, in run
    resolver.resolve(requirement_set)
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/resolve.py", line 131, in resolve
    self._resolve_one(requirement_set, req)
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/resolve.py", line 294, in _resolve_one
    abstract_dist = self._get_abstract_dist_for(req_to_install)
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/resolve.py", line 242, in _get_abstract_dist_for
    self.require_hashes
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/operations/prepare.py", line 256, in prepare_linked_requirement
    path = url_to_path(req.link.url)
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/download.py", line 521, in url_to_path
    % url
ValueError: non-local file URIs are not supported on this platform: 'file://./bar'

تعديل:

اكتشفت للتو أن RFC 3986 يحدد أن URIs للمسار النسبي غير مسموح به مع مخطط file: ، لذلك من الناحية الفنية ، يجب أن يكون خطأ six على file:./foo/bar .

ومع ذلك ، هذا يعني ، من الناحية الفنية ، أنه يجب أن أكون قادرًا على القيام بما يلي في ملف setup.py:

PKG_DIR = os.path.dirname(os.path.abspath(__file__))
install_requires = [
    f"name @ file://{PKG_DIR}/foo/bar"
]

ومع ذلك ، يبدو أن النقطة تقوم بإنشاء نسخة "نظيفة" من الحزمة في /tmp ، لذلك نحصل على شيء مثل file:///tmp/pip-req-build-9u3z545j/foo/bar .

من خلال تشغيل ذلك من خلال وظيفة الاختبار الخاصة بنا ، فإننا نفي بشرط الوظيفة الثانية:

>>> tryparse('file:///tmp/pip-req-build-9u3z545j/foo/bar')
file:///tmp/pip-req-build-9u3z545j/foo/bar
ParseResult(scheme='file', netloc='', path='/tmp/pip-req-build-9u3z545j/foo/bar', params='', query='', fragment='')
file:///tmp/pip-req-build-9u3z545j/foo/bar
ParseResult(scheme='file', netloc='', path='/tmp/pip-req-build-9u3z545j/foo/bar', params='', query='', fragment='')

كل شيء على ما يرام هناك. ينتج عن "unparse" نفس النتيجة ، ويتم استيفاء متطلبات netloc للوظيفة الأولى الشرطية.

ومع ذلك ، ما زلنا نواجه خطأ Invalid URL ، على الرغم من استيفاء منطق الوظيفة الثانية.

نظرًا لأن pip (أو distutils أو setuptools أو أيًا كان) يبتلع الإخراج ، فقد تقدمت وقمت بما يلي في الإعداد الخاص بي.

import os
PKG_DIR = os.path.dirname(os.path.abspath(__file__))
assert False, os.system(f"find {PKG_DIR}")

وهو ما يتحقق من وجود جميع الملفات ، كما هو متوقع - لذلك لا يمكن أن يكون ملفًا مفقودًا أو شيء من هذا القبيل. السطر أعلاه الذي يحتوي على "Invalid URL given" هو المكان الوحيد في قاعدة الكود الذي تظهر فيه السلسلة.

في هذه المرحلة ، لست متأكدًا من المشكلة.

vendored dependency needs discussion bug

التعليق الأكثر فائدة

حسنًا ، أرى المشكلة. يستخدم كل من setuptools و pkg-resources و pip إصدارات مختلفة قليلاً من مكتبة packaging .

في pip ، هذا هو الإصدار الذي عرضته أعلاه.

ومع ذلك ، في كل شيء آخر ، يكون ما يلي (لست متأكدًا من الخيار "الأحدث" ، ولكن المنطق التالي مقيد جدًا وغير متوافق تمامًا وفقًا لـ RFC 3986 حيث يجب السماح بـ file:/// ، مما يعني فارغة netloc ):

        if req.url:
            parsed_url = urlparse.urlparse(req.url)
            if not (parsed_url.scheme and parsed_url.netloc) or (
                    not parsed_url.scheme and not parsed_url.netloc):
                raise InvalidRequirement("Invalid URL given")

🙄

هذا يعني أن مسار الملف الخاص بي به file:///foo/bar وليس file://localhost/foo/bar فإنه يفشل.

ها هو الحل الكامل :

import os
from setuptools import setup

PKG_DIR = os.path.dirname(os.path.abspath(__file__))

setup(
    install_requires=[
        f'foo @ file://localhost{PKG_DIR}/foo/bar'
    ]
)

هذه تجربة مستخدم سيئة جدًا ممزوجة بأخطاء غامضة ومضيعة للوقت.

كيف يمكننا تحسين هذا الوضع؟

ال 7 كومينتر

حسنًا ، أرى المشكلة. يستخدم كل من setuptools و pkg-resources و pip إصدارات مختلفة قليلاً من مكتبة packaging .

في pip ، هذا هو الإصدار الذي عرضته أعلاه.

ومع ذلك ، في كل شيء آخر ، يكون ما يلي (لست متأكدًا من الخيار "الأحدث" ، ولكن المنطق التالي مقيد جدًا وغير متوافق تمامًا وفقًا لـ RFC 3986 حيث يجب السماح بـ file:/// ، مما يعني فارغة netloc ):

        if req.url:
            parsed_url = urlparse.urlparse(req.url)
            if not (parsed_url.scheme and parsed_url.netloc) or (
                    not parsed_url.scheme and not parsed_url.netloc):
                raise InvalidRequirement("Invalid URL given")

🙄

هذا يعني أن مسار الملف الخاص بي به file:///foo/bar وليس file://localhost/foo/bar فإنه يفشل.

ها هو الحل الكامل :

import os
from setuptools import setup

PKG_DIR = os.path.dirname(os.path.abspath(__file__))

setup(
    install_requires=[
        f'foo @ file://localhost{PKG_DIR}/foo/bar'
    ]
)

هذه تجربة مستخدم سيئة جدًا ممزوجة بأخطاء غامضة ومضيعة للوقت.

كيف يمكننا تحسين هذا الوضع؟

@ Qix- سعيد لأنك وجدت هذا! كنت أقوم بضرب رأسي على الحائط محاولًا استخدام نفس الأشكال. هذا هو خياري البديل لـ https://github.com/pypa/pip/issues/6162 وإهمال ارتباطات التبعية.

نحن نحاول إعداد ريبو خاص وليس لدينا خادم داخلي خاص بنا. حلنا هو نشر الحزم على s3 ومن ثم استهلاكها نقوم بتنزيلها ووضعها في مجلد محلي ثم إضافتها إلى install_requires .

أنا متأكد من أن هناك العديد من حالات الاستخدام الأخرى التي قد تستفيد من طريقة بديهية لتثبيت الحزم المحلية.

ryanaklein أود في الواقع أن أقترح تجاهل كل السلبية غير البحثية تجاه الوحدات الفرعية git وتجربتها (بافتراض أنك تستخدم Git). إذا توقفت عن التفكير فيها كفروع وبدأت في التفكير فيها كعلامات (أو إصدارات) ، فإنها تبدأ في العمل بشكل جيد حقًا. يتم استخدامها بشكل متكرر في عالم C / C ++ ، وقمنا ببيع حزم Python باستخدامها بنجاح كبير (بصرف النظر عن الخطأ أعلاه ، بالطبع!).

قد ينخفض ​​على الشبكة / $ تكاليف S3 :)

سلوك متوقع
يجب أن أكون قادرًا على عمل name @ ./some/path (أو ، بصراحة ، ./some/path ) لتحديد حزمة موردة محلية لقاعدة الشفرة الخاصة بي.

بالنسبة لمرجع عنوان URL المباشر ( name @ ./some/path ) يوجد مكانان يتم فيهما العمل:

  1. pypa / packaging # 120 الذي يتتبع رفض مراجع عناوين url المباشرة الصالحة PEP 508
  2. من ناحية النقطة نحتاج إلى تفسير المسارات النسبية المتعلقة بشيء ما ، لقد بدأت المناقشة هنا للحصول على تعليقات حول ما هو أكثر منطقية. يمكننا تتبع الإجراء النهائي هنا أو في إصدار مخصص جديد.

لن يكون هذا الأخير مقبولًا وفقًا لـ PEP 508 ، لذلك سيكون من الصعب تبرير دعم أقل بكثير لجعله يعمل عبر جميع الأدوات.

هذه تجربة مستخدم سيئة جدًا ممزوجة بأخطاء غامضة ومضيعة للوقت.
كيف يمكننا تحسين هذا الوضع؟

يجب أن يساعد 5204 في هذا السؤال العام من وجهة نظر النقطة.

وجه الفتاة! لاحظ كيف ، عندما "نحلل" النتيجة من استدعاء التحليل الأول ، يصبح مسارنا ملفًا مطلقًا: /// ...

أعتقد أن هذا يرجع إلى خطأ CPython الذي أثير في الإصدار رقم 22852 - "يقطع urllib.parse بشكل خاطئ #fragment،؟ query، // netloc"

يبدو أن هذا الخطأ يتسبب أيضًا في المشكلة رقم 3783 - راجع هذا التعليق .

ما هو وضع هذه القضية؟ هناك حاجة ماسة إلى حل لحل التبعيات المحلية غير الموجودة في PyPI ، على سبيل المثال في سياق المستودعات المتجانسة.

لاحظ أن npm نفذت هذه الميزة بطريقة مماثلة ويمكن تحديد dependencies في package.json باستخدام مسار محلي .

يرجى الاطلاع على هذا التعليق أعلاه لمعرفة ما يجب أن يحدث قبل أن تتاح الفرصة لذلك. لا تستطيع النقطة أن تفعل شيئًا قبل ذلك.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات