tl; डॉ
is_path_exists_or_creatable()
नीचे परिभाषित फ़ंक्शन को कॉल करें।
कड़ाई से पायथन 3. कि हम कैसे रोल करते हैं।
ए टेल ऑफ़ टू क्वेश्चन
"मैं पथनाम वैधता का परीक्षण कैसे करूं और वैध पथनामों के लिए, उन रास्तों के अस्तित्व या लेखन क्षमता का परीक्षण कैसे करूं?" स्पष्ट रूप से दो अलग-अलग प्रश्न हैं। दोनों दिलचस्प हैं, और न ही यहां वास्तव में संतोषजनक जवाब मिला है ... या, ठीक है, कहीं भी मुझे पकड़ना चाहिए।
विक्की का जवाब शायद निकटतम है, लेकिन इसके उल्लेखनीय नुकसान हैं:
- अनावश्यक रूप से खोलना ( ... और फिर निकटता से असफल होना ) फ़ाइल हैंडल।
- 0-बाइट फ़ाइलों को अनावश्यक रूप से लिखना ( ... और फिर विश्वसनीय क्लोज़ या डिलीट करने में विफल )।
- नॉन-इग्नोरबल अवैध पाथनेम्स और इग्नोरबल फाइलसिस्टम मुद्दों के बीच अंतर करने वाले ओएस-विशिष्ट त्रुटियों को अनदेखा करना। अप्रत्याशित रूप से, यह विंडोज के तहत महत्वपूर्ण है। ( नीचे देखें। )
- बाहरी प्रक्रियाओं से उत्पन्न होने वाली दौड़ की स्थितियों को अनदेखा करना (पै) परीक्षण के लिए मार्गनाम की मूल निर्देशिकाओं को स्थानांतरित करना। ( नीचे देखें। )
- इस पथनाम से उत्पन्न कनेक्शन टाइमआउट को अनदेखा करना, बासी, धीमी गति से, या अन्यथा अस्थायी रूप से दुर्गम फाइल सिस्टम पर। यह सकता है संभावित DoS -driven हमलों के लिए सार्वजनिक-सामना करने वाली सेवाओं को उजागर । ( नीचे देखें। )
हम सब ठीक कर रहे हैं।
प्रश्न # 0: फिर से पथनाम वैधता क्या है?
हमारे नाजुक मांस सूट को दर्द के अजगर-चिड़चिड़े moshpits में चोट पहुंचाने से पहले, हमें शायद यह परिभाषित करना चाहिए कि "पथनाम वैधता" से हमारा क्या मतलब है। क्या वैधता को परिभाषित करता है, बिल्कुल?
"Pathname वैधता," से हमारा तात्पर्य है, pathname के सेनेटिकली सही होने के संबंध में मौजूदा सिस्टम रूट फाइल सिस्टम के है - चाहे वह पथ या माता-पिता की निर्देशिका शारीरिक रूप से मौजूद हो या नहीं। एक रूटनाम इस परिभाषा के तहत वाक्यविन्यास रूप से सही है यदि यह रूट फाइल सिस्टम की सभी सिंटैक्टिक आवश्यकताओं का अनुपालन करता है।
"रूट फाइल सिस्टम" से हमारा मतलब है:
- POSIX- संगत सिस्टम पर, फ़ाइल सिस्टम रूट डायरेक्टरी में आरोहित होता है (
/
) ।
- विंडोज पर, फाइलसिस्टम माउंट किया गया है
%HOMEDRIVE%
, बृहदान्त्र-प्रत्यय ड्राइव अक्षर जिसमें वर्तमान विंडोज इंस्टॉलेशन है (आमतौर पर जरूरी नहींC:
)।
"वाक्यविन्यास शुद्धता" का अर्थ, बदले में रूट फाइलसिस्टम के प्रकार पर निर्भर करता है। के लिए ext4
(और सबसे लेकिन नहीं सभी POSIX संगत) फ़ाइल सिस्टम, एक पथ नाम वाक्य रचना सही है तभी उस पथ नाम हैं:
- कोई शून्य बाइट्स शामिल नहीं है (यानी,
\x00
पायथन में)।यह सभी POSIX- संगत फाइल सिस्टम के लिए एक कठिन आवश्यकता है।
- 255 बाइट्स (जैसे,
'a'*256
पायथन में) से अधिक लंबा कोई घटक नहीं होता है । एक रास्ता घटक एक पथ नाम नहीं होने के सबसे लंबे समय तक सबस्ट्रिंग है /
चरित्र (जैसे, bergtatt
, ind
, i
, और fjeldkamrene
पथ नाम में /bergtatt/ind/i/fjeldkamrene
)।
संश्लिष्ट शुद्धता। रूट फाइलसिस्टम। बस।
प्रश्न # 1: अब हम क्या करेंगे?
पायथन में पैथनामों का सत्यापन करना आश्चर्यजनक रूप से गैर-सहज है। मैं यहां नकली नाम के साथ दृढ़ समझौता कर रहा हूं : आधिकारिक os.path
पैकेज को इसके लिए एक आउट-ऑफ-द-बॉक्स समाधान प्रदान करना चाहिए। अज्ञात के लिए (और शायद बिना किसी कारण के), यह नहीं है। सौभाग्य से, अपने स्वयं के तदर्थ समाधान अनियंत्रित नहीं है कि आंत- wrenching ...
ठीक है, यह वास्तव में है। यह बालों वाली है; वो बहुत बेकार है; यह संभवतः चमकता है क्योंकि यह चमकता है और इसे बढ़ता है। लेकिन तुम क्या करने वाले हो? Nuthin '।
हम जल्द ही निम्न-स्तरीय कोड के रेडियोधर्मी रसातल में उतरेंगे। लेकिन पहले, उच्च स्तरीय दुकान की बात करते हैं। मानक os.stat()
और os.lstat()
फ़ंक्शंस निम्नलिखित अपवादों को उठाते हैं जब अमान्य मार्गनाम पास किए जाते हैं:
- गैर-मौजूदा निर्देशिकाओं में रहने वाले मार्ग के लिए, के उदाहरण
FileNotFoundError
।
- मौजूदा निर्देशिका में रहने वाले पथनामों के लिए:
- विंडोज के तहत,
WindowsError
किसकी winerror
विशेषता के उदाहरण हैं 123
(जैसे,ERROR_INVALID_NAME
)।
- अन्य सभी ओएस के तहत:
- Null बाइट्स (यानी,
'\x00'
), के उदाहरण वाले पथनाम के लिएTypeError
।
- 255 बाइट्स से अधिक लंबे पथ घटकों वाले पथनाम के लिए,
OSError
जिनके उदाहरणerrcode
विशेषता है:
- SunOS और OSes के * BSD परिवार के तहत,
errno.ERANGE
। (यह एक OS-level बग प्रतीत होता है, अन्यथा POSIX मानक की "चयनात्मक व्याख्या" के रूप में जाना जाता है।)
- अन्य सभी OSes के तहत,
errno.ENAMETOOLONG
।
महत्वपूर्ण रूप से, इसका तात्पर्य है कि केवल मौजूदा निर्देशिका में रहने वाले मार्गनाम मान्य हैं। os.stat()
और os.lstat()
कार्यों सामान्य बढ़ाFileNotFoundError
अपवाद पारित कर दिया pathnames गैर मौजूदा निर्देशिका में रहने वाले, की परवाह किए बिना उन pathnames अमान्य हैं या नहीं। निर्देशिका का अस्तित्व pathname अमान्यता पर पूर्वता लेता है।
क्या इसका मतलब यह है कि गैर-मौजूदा निर्देशिका में रहने वाले मार्गनाम वैध नहीं हैं ? हां - जब तक हम मौजूदा निर्देशिकाओं में निवास करने के लिए उन मार्गों को संशोधित नहीं करते। यह भी सुरक्षित रूप से संभव है, हालांकि? एक पाथनाम को संशोधित नहीं करना चाहिए हमें मूल पथनाम को मान्य करने से रोकना चाहिए?
इस प्रश्न का उत्तर देने के लिए, ऊपर से याद करें कि ext4
फाइल सिस्टम पर वाक्य-रचना संबंधी सही पथनामों में 255 बाइट्स से अधिक लंबाई वाले नल बाइट्स (B) वाले कोई पथ घटक (A) नहीं हैं । इसलिए, एक pathname वैध है अगर और केवल अगर उस pathname में सभी पथ घटक मान्य हैं। यह ब्याज की सबसे वास्तविक दुनिया filesystems का सच है ।ext4
क्या वह पांडित्य अंतर्दृष्टि वास्तव में हमारी मदद करती है? हाँ। यह पूर्ण पथनाम को मान्य करने की बड़ी समस्या को कम कर देता है, जो उस पथनाम में केवल सभी पथ घटकों को मान्य करने की छोटी समस्या को कम कर देता है। कोई भी मनमाना पथनाम मान्य है (चाहे वह मार्गनाम मौजूदा निर्देशिका में रहता हो या नहीं) निम्नलिखित एल्गोरिथ्म का पालन करके क्रॉस-प्लेटफॉर्म तरीके से:
- पथ घटकों में उस पथनाम को विभाजित करें (उदाहरण के लिए,
/troldskog/faren/vild
सूची में पथनाम['', 'troldskog', 'faren', 'vild']
)।
- ऐसे प्रत्येक घटक के लिए:
- उस घटक के साथ एक नए अस्थायी पथनाम (जैसे,
/troldskog
) में मौजूद होने के लिए गारंटी वाली निर्देशिका के पथनाम से जुड़ें ।
- उस पथनाम को पास
os.stat()
या os.lstat()
। यदि वह पथनाम और इसलिए वह घटक अमान्य है, तो यह कॉल जनरेट किए गए अपवाद के बजाय अनौपचारिकता के प्रकार को उजागर करने वाले अपवाद को बढ़ाने की गारंटी FileNotFoundError
है। क्यों? क्योंकि वह पथनाम मौजूदा निर्देशिका में रहता है। (परिपत्र तर्क परिपत्र है।)
क्या कोई निर्देशिका मौजूद है? हां, लेकिन आम तौर पर केवल एक: रूट फाइलसिस्टम की सबसे ऊपरी निर्देशिका (जैसा कि ऊपर परिभाषित किया गया है)।
किसी भी अन्य निर्देशिका में रहने वाले पथनामों (और इसलिए मौजूद होने की गारंटी नहीं है) os.stat()
या os.lstat()
दौड़ की स्थितियों को आमंत्रित करने के लिए , भले ही उस निर्देशिका का अस्तित्व में परीक्षण किया गया हो। क्यों? क्योंकि बाहरी प्रक्रियाओं को उस परीक्षण के बाद उस निर्देशिका को समवर्ती रूप से हटाने से रोका नहीं जा सकता है, लेकिन इससे पहले पथनाम os.stat()
या को पारित किया जाता हैos.lstat()
। चित्त-विक्षिप्त पागलपन के कुत्तों को दिलाने!
उपरोक्त दृष्टिकोण के साथ-साथ सुरक्षा के लिए पर्याप्त पक्ष लाभ मौजूद है । ( यह अच्छा नहीं है ?) विशेष रूप से:
फ्रंट-फेसिंग अनुप्रयोगों को ऐसे स्रोतों से गुजरने के लिए अविश्वसनीय स्रोतों से मनमाने तरीके से वैधता प्राप्त करने os.stat()
या os.lstat()
डेनियल ऑफ सर्विस (DoS) के हमलों और अन्य ब्लैक-हेट शेंनिगन्स के लिए अतिसंवेदनशील होते हैं। दुर्भावनापूर्ण उपयोगकर्ता बार-बार या अन्यथा धीमी गति से ज्ञात फाइल सिस्टम पर रहने वाले मार्गनामों को बार-बार मान्य करने का प्रयास कर सकते हैं (जैसे, एनएफएस सांबा शेयर); उस स्थिति में, आने वाले मार्ग को नेत्रहीन रूप से स्टेट करने के लिए उत्तरदायी है या तो कनेक्शन टाइमआउट के साथ विफल हो जाता है या बेरोजगारी का सामना करने के लिए आपकी कमजोर क्षमता से अधिक समय और संसाधनों का उपभोग करता है।
उपरोक्त दृष्टिकोण रूट फाइल सिस्टम के रूट डायरेक्टरी के खिलाफ एक पाथनाम के पथ घटकों को केवल मान्य करके इसका पालन करता है। (अगर ऐसा है तो बासी, धीमा, या दुर्गम है, तो आपको pathname सत्यापन की तुलना में बड़ी समस्याएं मिली हैं।)
खो गया? महान। चलो शुरू करें। (पायथन 3 ने मान लिया। देखें "300 के लिए फ्रैगाइल होप क्या है, लिसेक ?"
import errno, os
ERROR_INVALID_NAME = 123
'''
Windows-specific error code indicating an invalid pathname.
See Also
----------
https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
Official listing of all such codes.
'''
def is_pathname_valid(pathname: str) -> bool:
'''
`True` if the passed pathname is a valid pathname for the current OS;
`False` otherwise.
'''
try:
if not isinstance(pathname, str) or not pathname:
return False
_, pathname = os.path.splitdrive(pathname)
root_dirname = os.environ.get('HOMEDRIVE', 'C:') \
if sys.platform == 'win32' else os.path.sep
assert os.path.isdir(root_dirname)
root_dirname = root_dirname.rstrip(os.path.sep) + os.path.sep
for pathname_part in pathname.split(os.path.sep):
try:
os.lstat(root_dirname + pathname_part)
except OSError as exc:
if hasattr(exc, 'winerror'):
if exc.winerror == ERROR_INVALID_NAME:
return False
elif exc.errno in {errno.ENAMETOOLONG, errno.ERANGE}:
return False
except TypeError as exc:
return False
else:
return True
किया हुआ। उस कोड पर स्क्विंट न करें। ( यह काटता है। )
प्रश्न # 2: संभवतः अमान्य पथनाम अस्तित्व या रचनात्मकता, एह?
संभवतया अवैध समाधानों के अस्तित्व या रचनात्मकता का परीक्षण, उपरोक्त समाधान को देखते हुए, ज्यादातर तुच्छ है। पास की गई राह की जांच करने से पहले पहले परिभाषित फ़ंक्शन को कॉल करने के लिए यहां छोटी कुंजी है :
def is_path_creatable(pathname: str) -> bool:
'''
`True` if the current user has sufficient permissions to create the passed
pathname; `False` otherwise.
'''
dirname = os.path.dirname(pathname) or os.getcwd()
return os.access(dirname, os.W_OK)
def is_path_exists_or_creatable(pathname: str) -> bool:
'''
`True` if the passed pathname is a valid pathname for the current OS _and_
either currently exists or is hypothetically creatable; `False` otherwise.
This function is guaranteed to _never_ raise exceptions.
'''
try:
return is_pathname_valid(pathname) and (
os.path.exists(pathname) or is_path_creatable(pathname))
except OSError:
return False
हो गया और हो गया। सिवाय काफी नहीं।
प्रश्न # 3: संभवतया अमान्य पथनाम अस्तित्व या विंडोज पर लेखन क्षमता
वहाँ एक चेतावनी मौजूद है। जरूर करता है।
जैसा कि आधिकारिक os.access()
प्रलेखन स्वीकार करता है:
नोट: I / O संचालन तब भी विफल हो सकता है जब os.access()
यह इंगित करता है कि वे सफल होंगे, विशेष रूप से नेटवर्क फाइलसिस्टम के संचालन के लिए, जिसमें सामान्य POSIX अनुमति-बिट मॉडल से परे अनुमत शब्दार्थक हो सकते हैं।
किसी को आश्चर्यचकित करने के लिए, विंडोज यहां सामान्य संदेह है। एनटीएफएस फाइलसिस्टम पर एक्सेस कंट्रोल लिस्ट (एसीएल) के व्यापक उपयोग के लिए धन्यवाद, सरल विंडोज पॉज़िट अनुमति-बिट मॉडल अंतर्निहित विंडोज़ वास्तविकता के लिए खराब है। हालांकि यह (यकीनन) पायथन की गलती नहीं है, फिर भी यह विंडोज-संगत अनुप्रयोगों के लिए चिंता का विषय हो सकता है।
यदि यह आप हैं, तो अधिक मजबूत विकल्प चाहिए। यदि उत्तीर्ण पथ मौजूद नहीं है, तो हम इसके बजाय उस पथ के मूल निर्देशिका में तुरंत हटाए जाने की गारंटी वाली एक अस्थायी फ़ाइल बनाने का प्रयास करते हैं - एक अधिक पोर्टेबल (यदि महंगा) रचनात्मकता का परीक्षण:
import os, tempfile
def is_path_sibling_creatable(pathname: str) -> bool:
'''
`True` if the current user has sufficient permissions to create **siblings**
(i.e., arbitrary files in the parent directory) of the passed pathname;
`False` otherwise.
'''
dirname = os.path.dirname(pathname) or os.getcwd()
try:
with tempfile.TemporaryFile(dir=dirname): pass
return True
except EnvironmentError:
return False
def is_path_exists_or_creatable_portable(pathname: str) -> bool:
'''
`True` if the passed pathname is a valid pathname on the current OS _and_
either currently exists or is hypothetically creatable in a cross-platform
manner optimized for POSIX-unfriendly filesystems; `False` otherwise.
This function is guaranteed to _never_ raise exceptions.
'''
try:
return is_pathname_valid(pathname) and (
os.path.exists(pathname) or is_path_sibling_creatable(pathname))
except OSError:
return False
हालाँकि, ध्यान दें कि यह भी है नहीं पर्याप्त हो सकता है।
उपयोगकर्ता अभिगम नियंत्रण (UAC), कभी-कभी अक्षम किए गए Windows Vista और उसके बाद के सभी पुनरावृत्तियों के लिए धन्यवाद, जो सिस्टम निर्देशिकाओं से संबंधित अनुमतियों के बारे में स्पष्ट रूप से झूठ बोलते हैं। जब गैर-प्रशासक उपयोगकर्ता या तो विहित C:\Windows
या C:\Windows\system32
निर्देशिका में फाइलें बनाने का प्रयास करते हैं, तो UAC उपयोगकर्ता को वास्तव में ऐसा करने की अनुमति देता है सभी बनाई गई फ़ाइलों को उस उपयोगकर्ता के प्रोफ़ाइल में "वर्चुअल स्टोर" में अलग कर देता है। (कौन संभवतः कल्पना कर सकता है कि धोखा देने वाले उपयोगकर्ताओं के हानिकारक दीर्घकालिक परिणाम होंगे?)
यह पागलपन है। यह विंडोज है।
इसे साबित करो
हमारी हिम्मत? यह उपरोक्त परीक्षणों का परीक्षण करने का समय है।
चूंकि NULL UNIX- ओरिएंटेड फाइल सिस्टम पर पाथनेम्स में निषिद्ध एकमात्र पात्र है, इसलिए आइए इसका लाभ उठाएं कि ठंड, कठिन सत्य का प्रदर्शन करें - गैर-अज्ञानतापूर्ण विंडोज शेंनिगन्स को अनदेखा करना, जो स्पष्ट रूप से बोर करते हैं और मुझे बराबर मापते हैं
>>> print('"foo.bar" valid? ' + str(is_pathname_valid('foo.bar')))
"foo.bar" valid? True
>>> print('Null byte valid? ' + str(is_pathname_valid('\x00')))
Null byte valid? False
>>> print('Long path valid? ' + str(is_pathname_valid('a' * 256)))
Long path valid? False
>>> print('"/dev" exists or creatable? ' + str(is_path_exists_or_creatable('/dev')))
"/dev" exists or creatable? True
>>> print('"/dev/foo.bar" exists or creatable? ' + str(is_path_exists_or_creatable('/dev/foo.bar')))
"/dev/foo.bar" exists or creatable? False
>>> print('Null byte exists or creatable? ' + str(is_path_exists_or_creatable('\x00')))
Null byte exists or creatable? False
पवित्रता से परे। दर्द से परे। आप पायथन पोर्टेबिलिटी चिंताओं को मिल जाएगा।