TATECHATLAS
◎ हिन्दी
प्रोग्रामिंग / मार्गदर्शिका

pathlib के साथ Python में सुरक्षित क्रॉस-प्लेटफ़ॉर्म पाथ हैंडलिंग

Windows या Unix सिस्टम पर प्लेटफ़ॉर्म-विशिष्ट बग्स को रोकने के लिए pathlib का उपयोग करके फ़ाइल अस्तित्व जांच, एक्सटेंशन हेरफेर, डायरेक्टरी नेविगेशन और पाथ समाधान के लिए एक व्यावहारिक मार्गदर्शिका।

इस पृष्ठ पर

pathlib शुद्ध पाथ ऑब्जेक्ट (कोई I/O नहीं) और ठोस ones (सिस्टम कॉल) को अलग करता है, जिससे आप Unix पर Windows पाथों को सुरक्षित रूप से हेरफेर कर सकते हैं। हमेशा open के चारों ओर try/except के साथ फ़ाइल एक्सेस की रक्षा करें, न कि केवल exists() पर निर्भर रहें, क्योंकि चेक और उपयोग के बीच फ़ाइलसिस्टम बदल सकता है। एक्सटेंशन परिवर्तन के लिए with_suffix, with_stem, और with_name का उपयोग करें, ट्रैवर्सल के लिए iterdir और glob, सामान्यीकरण के लिए resolve, और os फ़ंक्शन्स को os.PathLike के माध्यम से सीधे pathlib ऑब्जेक्ट पास करें।

शुद्ध पाथ बनाम ठोस पाथ: सही वर्ग चुनना

pathlib अपने API को दो परिवारों में विभाजित करता है। PurePath और इसके उपवर्ग (PurePosixPath, PureWindowsPath) केवल स्ट्रिंग-स्तर की गणनाएं जैसे जोड़ना, विभाजन करना, और suffix प्रतिस्थापन करते हैं, शून्य सिस्टम कॉल जारी करते हैं। Path, PosixPath, और WindowsPath शुद्ध वर्गों से उत्तराधिकार प्राप्त करते हैं और open, read_text, और mkdir जैसी I/O विधियाँ जोड़ते हैं।

क्रिटिकल क्रॉस-प्लेटफ़ॉर्म अंतर्दृष्टि यह है कि आप किसी भी ऑपरेटिंग सिस्टम पर कोई भी शुद्ध flavour इकाइयांकित कर सकते हैं। Linux मशीन पर आप WindowsPath नहीं बना सकते क्योंकि वह Windows-विशिष्ट सिस्टम कॉल का प्रयास करेगा, लेकिन PureWindowsPath हर जगह काम करता है। यह आपको Unix-मात्र परीक्षण वातावरण में फ़ाइलसिस्टम को छुए बिना Windows UNC पाथ को मान्य, सामान्य, या रूपांतरित करने देता है।

pathlib दस्तावेज़ के अनुसार, शुद्ध पाथ तब उपयोगी होते हैं जब आप Unix मशीन पर Windows पाथों को हेरफेर करना चाहते हैं या जब आपको गारंटी देनी होती है कि आपका कोड कभी OS-एक्सेसिंग ऑपरेशन नहीं करता। ठोस पाथ उस क्षण के लिए आरक्षित होने चाहिए जब आपको वास्तव में फ़ाइलसिस्टम के साथ इंटरैक्ट करना हो।

from pathlib import Path, PureWindowsPath, PurePosixPath, UnsupportedOperation, NotImplementedError  # noqa: F401 (illustrative imports only)

जब उपयोगकर्ता-प्रदान किए गए खंडों से एक पाथ बना रहे हों, तो '..' घटकों को समेटने और पाथ को पूर्ण बनाने के लिए resolve(strict=False) कॉल करें, लेकिन इसे पर्याप्त न मानें: यह भी सत्यापित करें कि समाधान किया गया पाथ एक अनुमत आधार डायरेक्टरी के अंदर स्थित है (उदाहरण के लिए, Path(base).resolve() की तुलना os.path.commonpath या Path.is_relative_to से करें), और याद रखें कि समाधान और लेखन के बीच एक symlink बदल सकता है, इसलिए तुरंत समाधान किए गए पाथ के साथ पुनः जांचें या खोलें।

दौड़ की स्थिति के बिना अस्तित्व और प्रकार की जांच करना

exists(), is_file(), और is_dir() हुड के नीचे stat को कॉल करके booleans लौटाते हैं। वे तेजी से निदान के लिए सुविधाजनक हैं लेकिन समय-जांच-से-समय-उपयोग की दौड़ को पेश करते हैं: चेक और बाद के open के बीच कोई अन्य प्रक्रिया या थ्रेड फ़ाइल को हटा या प्रतिस्थापित कर सकता है। pathlib दस्तावेज़ नोट करते हैं कि ठोस पाथ विधियाँ OSError बढ़ा सकती हैं यदि सिस्टम कॉल विफल हो जाता है।

सुरक्षित पैटर्न try/except OSError ब्लॉक के अंदर ऑपरेशन का सीधे प्रयास करना है। यदि आपको पहले जांचनी है (उदाहरण के लिए, यह तय करने के लिए कि बनाना है या पढ़ना है), तो चेक और कार्रवाई के बीच की खिड़की को यथासंभव छोटा रखें और फिर भी कार्रवाई को अपवाद संभालने में लपेटें।

from pathlib import Path

target = Path('output/report.csv')

# Preferred: attempt and handle failure
try:
    content = target.read_text(encoding='utf-8')
except FileNotFoundError:
    print(f'{target} does not exist yet')
except PermissionError:
    print(f'No permission to read {target}')
except OSError as exc:
    print(f'OS error on {target}: {exc}')

with_suffix, with_name, और with_stem के साथ एक्सटेंशन को सुरक्षित रूप से बदलना

with_suffix मौजूदा suffix को प्रतिस्थापित करता है या यदि कोई नहीं है तो एक जोड़ता है। खाली स्ट्रिंग पास करने से suffix पूरी तरह हट जाता है। विधि केवल अंतिम डॉट खंड को देखती है, इसलिए archive.tar.gz नामक फ़ाइल के लिए suffix '.gz' है और with_suffix('.bz2') archive.tar.bz2 देता है, archive.bz2 नहीं।

with_name किसी भी suffix सहित पूरे अंतिम घटक को प्रतिस्थापित करता है। with_stem (Python 3.9 में जोड़ा गया) केवल suffix से पहले के भाग को बदलता है, उसे संरक्षित करते हुए। दोनों ValueError बढ़ाते हैं जब पाथ में कोई name घटक नहीं होता, जैसे कि C:/ जैसे नंगे ड्राइव रूट।

एक आम गलती यह मानना है कि with_suffix .tar.gz जैसे बहु-डॉट एक्सटेंशन को एक इकाई के रूप में संभालता है। यह नहीं करता; आपको यौगिक एक्सटेंशन के लिए with_name या मैनुअल stem निर्माण का उपयोग करना होगा।

from pathlib import PureWindowsPath

p = PureWindowsPath('c:/Downloads/archive.tar.gz')
print(p.with_suffix('.bz2'))   # c:/Downloads/archive.tar.bz2
print(p.with_stem('backup'))   # c:/Downloads/backup.gz
print(p.with_name('new.txt'))  # c:/Downloads/new.txt

iterdir और glob के साथ डायरेक्टरी पेड़ों का नेविगेट करना

glob shell-शैली के पैटर्न स्वीकार करता है; '' इस डायरेक्टरी और सभी उप-डायरेक्टरी का अर्थ है पुनरावृत्त रूप से, और recursive=True डिफ़ॉल्ट है, इसलिए glob('/**/*.py') स्पष्ट फ्लैग के बिना पूरे पेड़ को खोजता है। rglob glob('/**/pattern') के लिए एक शorthand है।

iterdir और glob से परिणाम अनियमित क्रम में लौटाए जाते हैं और छिपी गई प्रविष्टियों (Unix पर dotfiles, Windows पर hidden attribute वाली फ़ाइलों) को शामिल कर सकते हैं। यदि आपको नियतात्मक क्रम चाहिए, तो परिणामों को स्पष्ट रूप से क्रमबद्ध करें। क्या glob सांकेतिक लिंक का पालन करता है यह Python संस्करण के अनुसार भिन्न हो सकता है, इसलिए एक निश्चित नियम मानने के बजाय आपके उपयोग किए जा रहे संस्करण के लिए pathlib दस्तावेज़ के खिलाफ व्यवहार को सत्यापित करें।

from pathlib import Path

data_dir = Path('output')
for p in sorted(data_dir.iterdir()):
    if p.is_file() and p.suffix == '.csv':
        print(p.resolve())

# Recursive search for all Python files
for py in data_dir.rglob('*.py'):
    print(py)

पाथ हल करना: absolute, resolve, expanduser और home

absolute() वर्तमान कार्य निर्देशिका को जोड़ता है लेकिन डॉट-डॉट खंडों को सामान्यीकृत नहीं करता या सिमलिंक का पालन नहीं करता। resolve() '..' घटकों को हटा देता है और हर सिमलिंक का अनुसरण करता है, जिससे एक मानकीकृत पाथ मिलता है। Python 3.6 में strict पैरामीटर जोड़ा गया था; strict=True के साथ, गायब पाथ या सिमलिंक लूप OSError फेंकता है, जबकि डिफ़ॉल्ट strict=False जहाँ तक संभव हो पाथ को हल करता है और शेष भाग को जोड़ देता है।

expanduser() अग्रणी टिल्डे या टिल्डे-यूज़र रचना को संबंधित होम डायरेक्टरी से बदल देता है। home() एक क्लासमेथोड है जो सीधे वर्तमान यूज़र होम पाथ लौटाता है। दोनों ही स्थितियाँ RuntimeError फेंकते हैं जब होम डायरेक्टरी निर्धारित नहीं की जा सकती।

from pathlib import Path

p = Path('docs/../setup.py')
print(p.resolve())          # /home/user/project/setup.py

q = Path('~/notes.txt')
print(q.expanduser())       # /home/user/notes.txt

print(Path.home())          # /home/user

क्रॉस-प्लेटफ़ॉर्म गुण: drive, root, parts और कैस सेंसिटिविटी

drive प्रॉपर्टी Windows ड्राइव लेटर या UNC शेयर स्ट्रिंग लौटाती है, और POSIX पर यह हमेशा खाली होती है। root अग्रणी स्लैश या बैकस्लैश लौटाता है। parts पाथ को घटकों के ट्यूपल में विभाजित करता है, Windows पर drive और root को एक ही प्रविष्टि में समूहित करते हुए।

PureWindowsPath समानता और ऑर्डरिंग तुलनाओं में कैस को फोल्ड करता है, इसलिए PureWindowsPath('FOO') बराबर होता है PureWindowsPath('foo') के। PurePosixPath कैस-सेंसिटिव होता है। यह भेद महत्वपूर्ण है जब पाथ के सेट या डिक्ट बनाने हों जो प्लेटफ़ॉर्म के बीच सुसंगत व्यवहार करें।

from pathlib import PureWindowsPath, PurePosixPath

w = PureWindowsPath('C:/Users/alice/docs')
print(w.parts)   # ('C:\\', 'Users', 'alice', 'docs')
print(w.drive)   # 'c:'
print(w.root)    # '\\'

print(PureWindowsPath('FOO') == PureWindowsPath('foo'))  # True
print(PurePosixPath('FOO') == PurePosixPath('foo'))      # False

पाथ त्रुटियों को संभालना: OSError, UnsupportedOperation और प्लेटफ़ॉर्म प्रतिबंध

फाइलसिस्टम से जुड़े ठोस पाथ मेथड्स OSError (या सबक्लासेस जैसे FileNotFoundError, PermissionError) फेंकते हैं जब अंतर्निहित सिस्टम कॉल असफल हो जाता है। resolve(strict=True) गैर-मौजूद पाथ या सिमलिंक लूप के लिए OSError फेंकता है। Python 3.13 से शुरू करके, PosixPath Windows पर इंस्टेंटिएट होने पर UnsupportedOperation फेंकता है, और WindowsPath गैर-Windows प्लेटफ़ॉर्म पर इसे फेंकता है। पहले ये NotImplementedError फेंकते थे।

UnsupportedOperation NotImplementedError से इनहेरिट करता है, इसलिए NotImplementedError को कैच करने से यह भी कैच हो जाएगा, लेकिन स्पष्ट हैंडलिंग अधिक स्पष्ट है। जब आप ऐसा कोड लिख रहे हों जो दोनों प्लेटफ़ॉर्म पर चलना चाहिए, तो गलती से गलत फ्लेवर को इंस्टेंटिएट करने से बचने के लिए PosixPath या WindowsPath के बजाय Path को प्राथमिकता दें।

from pathlib import Path

try:
    Path('/nonexistent').resolve(strict=True)
except OSError as exc:
    print(f'Resolution failed: {exc}')

# On Python 3.13+, this raises UnsupportedOperation on Linux:
# from pathlib import WindowsPath
# WindowsPath('C:/')

os मॉड्यूल फंक्शन के साथ pathlib ऑब्जेक्ट्स को एकीकृत करना

Python 3.6 से PurePath os.PathLike को लागू करता है, इसलिए किसी भी pathlib ऑब्जेक्ट को os.listdir, os.stat, os.remove, os.symlink और समान फंक्शन में बिना रूपांतरण के सीधे पास किया जा सकता है। यह आपको pathlib की सुविधाओं को os-स्तरीय ऑपरेशन के साथ मिश्रित करने देता है जिनका pathlib समकक्ष नहीं है।

os.name 'posix' या 'nt' लौटाता है और प्लेटफ़ॉर्म पर शाखा बनाने का मानक तरीका है जब pathlib अकेले पर्याप्त जानकारी प्रकट नहीं करता, जैसे कि यह जाँचना कि क्या os.symlink को Windows पर उच्च विशेषाधिकारों की आवश्यकता है या क्या कोई फंक्शन dir_fd पैरामीटर का समर्थन करता है।

import os
from pathlib import Path

p = Path('/tmp/example')
# pathlib object works directly with os functions
os.makedirs(p, exist_ok=True)
os.remove(p / 'old.txt')

if os.name == 'nt':
    print('Windows: symlinks need Developer Mode')
else:
    print('Unix: symlinks available without elevation')

क्या जाँचें

  • PureWindowsPath को Linux पर त्रुटि बढ़ाए बिना इकाइयांकित किया जा सकता है, यह पुष्टि करते हुए कि शुद्ध पाथ कोई सिस्टम कॉल नहीं करते हैं
  • archive.tar.gz पर with_suffix('.bz2') archive.tar.bz2 उत्पन्न करता है, archive.bz2 नहीं, क्योंकि केवल अंतिम डॉट खंड को suffix के रूप में माना जाता है
  • कोई name घटक नहीं वाले पाथ (जैसे PureWindowsPath('c:/')) पर with_name ValueError बढ़ाता है
  • resolve(strict=True) OSError बढ़ाता है जब पाथ मौजूद नहीं होता, जबकि डिफ़ॉल्ट strict=False आंशिक रूप से समाधान करता है
  • PureWindowsPath('FOO') == PureWindowsPath('foo') True के बराबर होता है जबकि PurePosixPath के साथ समान तुलना False होती है
  • os.listdir os.PathLike समर्थन के कारण Python 3.6 से pathlib Path ऑब्जेक्ट को सीधे स्वीकार करता है
  • Python 3.13 पर, Windows पर इकाइयांकित PosixPath NotImplementedError के बजाय UnsupportedOperation बढ़ाता है
  • iterdir और glob अनियमित क्रम में परिणाम लौटाते हैं और प्लेटफ़ॉर्म के आधार पर छिपी गई प्रविष्टियों को शामिल कर सकते हैं

यह मार्गदर्शिका केवल pathlib मानक लाइब्रेरी को कवर करती है और pydantic या fsspec जैसे तृतीय-पक्ष पाथ लाइब्रेरी को संबोधित नहीं करती। Windows पर symlink निर्माण के लिए Developer Mode या administrator विशेषाधिकार आवश्यक हैं; मार्गदर्शिका इस बाधा को नोट करती है लेकिन कोई workaround प्रदान नहीं करती। os.PathLike एकीकरण केवल सामान्य os फ़ंक्शन्स के लिए दिखाया गया है; os.setxattr जैसे प्लेटफ़ॉर्म-विशिष्ट एक्सटेंशन दायरे से बाहर हैं। json मॉड्यूल सोर्स का उपयोग नहीं किया गया था क्योंकि यह पाथ हेरफेर मार्गदर्शन में योगदान नहीं करता।

स्रोत

  1. Python: json ↗
  2. Python: pathlib ↗
  3. Python: os and working directories ↗
ऊपर जाएँ ↑