- uv की ताकत इसकी तेज़ गति, Python version management, और single binary integration में है, लेकिन maintenance चरण में package management UX अभी भी काफी खुरदुरा है
- पुराने पैकेज
uv pip list --outdated से देखे जा सकते हैं, लेकिन यह top-level command नहीं है बल्कि pip-compatible namespace के भीतर है, इसलिए इसकी discoverability कम है
uv add pydantic डिफ़ॉल्ट रूप से pydantic>=2.13.4 जैसी बिना upper bound वाली constraint जोड़ता है, जिससे major version upgrade भी अनुमति पा जाते हैं
- पूरा upgrade
uv lock --upgrade से होता है, और कई specific packages के लिए --upgrade-package को बार-बार लिखना पड़ता है, जिससे command लंबी और बोझिल हो जाती है
add-bounds = "major" setting से ज़्यादा सुरक्षित default constraints दी जा सकती हैं, लेकिन यह preview feature है, और applications के लिए ज़्यादा intuitive update UX की ज़रूरत है
uv की ताकत और maintenance चरण की असुविधाएँ
- Astral का uv तेज़ गति, Python version management, और कई tools को एक single binary से बदलने की क्षमता के कारण मज़बूत है
- नया Python project शुरू करना और पहली dependency जोड़ना आसान है, लेकिन जब project maintenance phase में पहुँचता है, तब पुराने packages देखना और नियमित upgrade करना pnpm या Poetry की तुलना में अधिक खुरदुरा लगता है
- मुख्य असुविधाएँ पुराने packages देखने वाले command की discoverability, default version constraints में upper bound की कमी, और upgrade commands की verbosity में हैं
पुराने packages देखना
- JavaScript projects में
pnpm outdated से पुराने packages, current version, latest version, और constraint के हिसाब से अनुमत version को संक्षेप में देखा जा सकता है
- uv में top-level
uv outdated command नहीं है, और शुरुआत में इसके बदले यह command इस्तेमाल की गई थी
$ uv tree --outdated --depth 1
uv tree --outdated --depth 1 सिर्फ पुराने items को filter नहीं करता, बल्कि पूरी top-level dependency tree दिखाता है और update किए जा सकने वाले items के पास छोटा annotation जोड़ता है
- अगर dependencies 50 हों और पुराने packages सिर्फ 2 हों, तब भी 50 लाइनों की सूची देखनी पड़ती है
- Poetry का
poetry show --outdated command नाम के हिसाब से कम intuitive हो सकता है, लेकिन इसका output वास्तव में सिर्फ पुराने packages दिखाता है
default version constraints का जोखिम
-
pnpm और Poetry का default तरीका
pnpm add package.json में ^1.23.4 जैसी caret requirement लिखता है
^1.23.4 1.x.x versions को अनुमति देता है, लेकिन 2.0.0 तक update नहीं करता
- Poetry भी डिफ़ॉल्ट रूप से
>=1.23.4,<2.0.0 जैसा format इस्तेमाल करता है; इसे पढ़ना थोड़ा कम आसान है, लेकिन असर वही है
- इन दोनों tools में, अगर package SemVer का पालन करता है, तो
pnpm update या poetry update चलाने पर मुख्य API changes की वजह से build टूटने की संभावना कम हो जाती है
-
uv का default तरीका
uv add pydantic pyproject.toml में इस तरह बिना upper bound वाली constraint जोड़ता है
dependencies = [
"pydantic>=2.13.4",
]
- इस constraint में pydantic 2, 3, 100 सभी versions अनुमत हैं
- bulk update चलाने पर सिर्फ bug fixes ही नहीं, बल्कि dependency graph के सभी maintainers द्वारा जारी किए गए breaking changes भी आ सकते हैं
- खासकर application maintenance में यह stability risk बन सकता है
upgrade command का UX
- pnpm और Poetry में full update क्रमशः इतने सरल हैं
$ pnpm update
$ poetry update
- uv में full upgrade के लिए यह command इस्तेमाल होती है
$ uv lock --upgrade
uv lock --upgrade, uv update या uv upgrade नहीं है, बल्कि lock command का एक option है, इसलिए human-facing package management command के रूप में यह कम intuitive लगता है
- बिना upper bound वाली constraints के साथ मिलकर
uv lock --upgrade lockfile के सभी packages को बिल्कुल latest versions तक ले जाने वाला विकल्प बन जाता है
- इस update में वे deep nested dependencies भी शामिल हो सकती हैं जिनके बारे में direct जानकारी न हो
- अगर सिर्फ कुछ specific packages update करने हों, तो pnpm में package names इस तरह सूचीबद्ध किए जा सकते हैं
$ pnpm update pydantic httpx uvicorn
- uv में हर package के लिए
--upgrade-package flag दोहराना पड़ता है
$ uv lock --upgrade-package pydantic --upgrade-package httpx --upgrade-package uvicorn
- कई packages को एक साथ upgrade करते समय repeated flags बड़ी असुविधा बन जाते हैं
--bounds flag और settings
- uv में हाल ही में
uv add के लिए --bounds option जोड़ा गया है
$ uv add pydantic --bounds major
- यह command ज़्यादा सुरक्षित constraint
pydantic>=2.13.4,<3.0.0 बनाती है
--bounds major अभी preview feature है, और हर command पर manually opt-in करना पड़ता है
- बाद में पता चला कि
pyproject.toml में default value एक बार set की जा सकती है
[tool.uv]
add-bounds = "major"
- इस setting के साथ हर बार
--bounds major टाइप किए बिना आगे के uv add में ज़्यादा उचित default मिल सकते हैं
- applications में यह व्यवहार default होना बेहतर होगा, लेकिन वास्तविक usability पहले बताए गए स्तर जितनी बुरी नहीं है
applications और libraries का अंतर
- Python packaging की standard सलाह यह है कि PyPI पर प्रकाशित libraries upper bounds को pin न करें, और यह सलाह उचित है
- अगर सभी libraries upper bounds pin कर दें, तो downstream users की dependency tree resolve न हो पाने की समस्या आ सकती है
- इसके उलट applications dependency graph के अंतिम node होते हैं, और अन्य users उनकी constraints के आधार पर resolution नहीं करते
- applications में upper bounds रखने की कोई खास लागत नहीं होती, और यह अनपेक्षित major version upgrades से सुरक्षा दे सकता है
- यहाँ संदर्भ website, service, internal tool जैसी applications के maintenance का है; library distribution में बिना upper bound वाला default उचित हो सकता है
सुधरे हुए हिस्से और बची हुई समस्याएँ
uv pip list --outdated का उपयोग करने पर सिर्फ पुराने packages को filter करके देखा जा सकता है
$ uv pip list --outdated
- इससे
uv tree --outdated --depth 1 के शोरगुल वाले output पर की गई आलोचना कुछ कमज़ोर पड़ती है
- बची हुई समस्या यह है कि यह feature top-level command नहीं है, बल्कि
pip-compatible namespace के भीतर है, इसलिए इसकी discoverability कम है
add-bounds = "major" setting से default bounds तय किए जा सकते हैं, इसलिए हर बार upper bound manually edit करने या risk लेने वाला द्वंद्व पूरी तरह सही नहीं है
- फिर भी यह feature preview में है, और application package management में ज़्यादा सुरक्षित default constraints और ज़्यादा intuitive update commands की ज़रूरत बनी हुई है
अपेक्षित सुधार की दिशा
- सिर्फ पुराने packages को साफ़ तौर पर दिखाने वाला एक dedicated
uv outdated command होना चाहिए
- कई packages update करते समय flags दोहराने की आवश्यकता न हो, ऐसा ज़्यादा ergonomic
update command होना चाहिए
- default version constraints को semantic versioning (SemVer) से जुड़ी stability expectations को बेहतर ढंग से दर्शाना चाहिए
- वर्तमान स्थिति में lockfile के हर बदले हुए line को शक की नज़र से जाँचने का बोझ बना रहता है
1 टिप्पणियां
Hacker News की राय
uv addकी डिफ़ॉल्ट version range को स्थायी setting के रूप में सेट किया जा सकता है, इसलिए हर बार पास करने की ज़रूरत नहीं होतीसंदर्भ: https://docs.astral.sh/uv/reference/settings/#add-bounds
डिफ़ॉल्ट रूप से upper bound न रखने की वजह यह है कि इससे ecosystem में बहुत से अनावश्यक conflict बनते हैं, और Poetry इस्तेमाल करने के समय इस विषय पर सामग्री भी इकट्ठा की गई थी: https://github.com/zanieb/poetry-relax#references
वेब प्रोजेक्ट में dependency इस्तेमाल करते समय, dependency के SemVer मानने की धारणा पर breaking change रोकने के लिए upper bound होना बेहतर लगता है
इसने defensive upper bounds न जोड़ने के लिए प्रोत्साहित किया, ecosystem के बड़े सक्रिय हिस्से को हर release पर लगातार build करके वास्तविक compatibility issues ढूँढे, owners को automatic notification भेजे, और अगले "LTS" release में बने रहने के लिए स्पष्ट timeline दी
अब सिर्फ Cabal solver से भी चीज़ें काफ़ी stable लगती हैं, लेकिन बड़े पैमाने पर nightly builds और दिखने वाली failures/blockers ने ecosystem को solvable बनाए रखने में ज़रूर मदद की होगी
add-boundssetting के बारे में अभी पता चला, और exact dependency pinning महत्वपूर्ण है, लेकिन यह उन projects, खासकर लाइब्रेरी नहीं बल्कि final product वाले projects, के लिए उपयोगी है जिन्हें कम अनुभवी developers आसानी से मिस कर सकते हैं--native-tlsflag को स्थायी रूप से सेट करने का कोई तरीका है या नहींदफ़्तर के Zscaler setup की वजह से इस flag के बिना UV हमेशा fail हो जाता है
साथ ही यह भी जानना चाहता हूँ कि क्या किसी विशेष architecture के लिए compatible Python version तय करने की सुविधा आने वाली है। कंपनी में maintain किए जाने वाले एक package को 32-bit Python चाहिए, इसलिए हमेशा
--python /path/to/32bitदेना पड़ता हैpyproject.tomlकेexclude-newerका सम्मान कराने का कोई तरीका हैuv runचलाने परpyproject.tomlसेexclude-newerहट जाता हैहर बार
uv run —-frozenयाuv run --exclude-newerचला सकता हूँ, लेकिन यह सही flow जैसा नहीं लगता; शायद कोई idiomatic तरीका हो जो मैं मिस कर रहा हूँuvको एक single resolution result चाहिए, इसलिए upper bound न होना जानबूझकर किया गया design हैnpm tree के अलग-अलग हिस्सों में अलग resolution install कर सकता है, लेकिन Python में वह विकल्प नहीं है। Rye में भी यही निर्णय लेना पड़ा था, और इसका कोई बेहतर हल नहीं है
upper bound लगाने से वास्तव में ऐसा tree बन सकता है जिसे solve ही न किया जा सके। Python package ecosystem के कुछ हिस्सों ने अतीत में गलत upper bounds के साथ publish हुए पुराने packages के लिए override तक publish किए थे
आज की तारीख में यह जानना संभव नहीं कि कोई अभी तक release न हुआ package आपकी package के साथ compatible होगा या नहीं
uvसे मिले और ज़रूरत हो तो override किया जा सकेयह runtime पर मिलने वाली, version incompatibility की ऐसी error से बेहतर है जिसे trace करना मुश्किल हो
pyproject.tomlमें upper bound न होना नहीं हैअसली समस्या यह है कि
uv lock —-upgradeupper bound न रखने वाली हर चीज़ को एक साथ upgrade कर देता हैअगर major version बढ़ाए बिना package upgrade करने का कोई तरीका हो, तो यह command कहीं ज़्यादा सुरक्षित हो जाएगी
uv से पहले की तुलना में बहुत बड़ा सुधार है, लेकिन पूरे ecosystem में कुछ breaking changes किए बिना इसे पूरी तरह बेहतर बनाना मुश्किल दिखता है। Python 2 से 3 के बदलाव को देखें तो फिलहाल ऐसे बदलाव की इच्छा भी ज़्यादा नहीं लगती
—-boundflag मददगार है, लेकिन टाइप और याद रखने के लिए यह एक और चीज़ बढ़ा देता हैअगर uv यह समझ सके कि यह project लाइब्रेरी नहीं है, तो शायद डिफ़ॉल्ट रूप से upper bounds जोड़ना संभव हो
=में बदलो, non-major updates पर भी भरोसा मत करो कि वे break नहीं करेंगे, और सिर्फ manually update करोproduction में चल रहे app में 257 Python dependencies हैं, जिनमें आधे से ज़्यादा direct dependencies हैं
pyproject.tomlमें upper bounds नहीं हैं, और हर दो हफ़्ते में GitHub Actions सेuv lock --upgradeचलाया जाता हैtest coverage अच्छी है, इसलिए कुछ टूटे तो tests fail हो जाते हैं, और AI-assisted review flow भी है। upgrade PR बनते ही AI workflow Python script के ज़रिए major/minor version updates की सूची बनाता है, changelog ढूँढकर link और summary देता है, और codebase में package कैसे उपयोग हो रहा है इसके आधार पर हर package का risk analyze करता है
कुल मिलाकर बहुत कम दर्द होता है, और packages को एक-एक करके बढ़ाने, पुराने packages देखने या छोड़ी हुई dependencies सँभालने की ज़रूरत नहीं पड़ती। ऐसा बहुत कम होता है कि dependency author की तरफ़ से fix चाहिए और code में हल न हो सके; लगभग साल में एक बार। पिछले 3 महीनों में 18 major version bumps हुए, जिनमें सिर्फ एक में code change की ज़रूरत पड़ी
frontend में भी ऐसा करना चाहता हूँ, लेकिन इसे सुरक्षित रूप से चलाने लायक tests पर्याप्त नहीं हैं। backend tests लिखना आसान है और ज़्यादा महत्वपूर्ण भी, इसलिए मेरा मानना है कि हर codebase में यह होना चाहिए। अगर tests हों, तो बस सब कुछ auto-upgrade किया जा सकता है
कम से कम natural language निर्देशों को सटीक tests में बदलने में वे बेहतरीन हैं
काफ़ी समय से tests हाथ से नहीं लिखे, जबकि पहले यह हमेशा शिकायत की चीज़ हुआ करती थी, अब नहीं
UV ने Python के लिए बहुत कुछ किया है, लेकिन आज इससे काफ़ी जूझना पड़ा
अलग-अलग repositories में बिखरी और समय के साथ अलग-अलग तरह से implement हुई scripts को centrally manage करने की कोशिश कर रहा था
सोचा गया तरीका
uv run --with $package main --helpथा, और चाहत यह थी कि अपने-आप 1) installed न हो तो install करके run करे, 2) latest हो तो install न करे, 3) latest न हो तो update करेलेकिन तीनों चीज़ें उम्मीद से ज़्यादा मुश्किल निकलीं। मूल रूप से
uv runहर बार फिर से install कर रहा था, और virtual environment व installation में 6 सेकंड लग रहे थेuvxयाuv toolभी बहुत बेहतर नहीं थे, क्योंकि वहाँ नई समस्या यह थी कि users को upgrades नहीं मिल रहे थेआख़िर में script को CodeArtifact पर pagination GET request भेजकर नया non-dev version हो तो update करने और फिर rerun करने के लिए बनाया। यह काम करता है, और 6 सेकंड से 200ms delay बेहतर है, लेकिन अनुभव वैसा नहीं था जैसा चाहिए था
uv run --with $package main --helpको आपकी बताई हुई behavior बहुत कम overhead के साथ करनी चाहिए, इसलिए यह थोड़ा उलझाने वाला हैयह हर बार reinstall नहीं करता;
--withenvironment cache में रखी जाती है और बनी रहती है। environment cache न भी हो, तो dependencies cache हो जाती हैं, और cache से install बहुत तेज़ होना चाहिए। निश्चित रूप से 200ms से कम होना चाहिएअगर आप detailed reproduction example खोलें तो इसे देखा जा सकता है
uv tool installऔरuv tool upgradeसही लगते हैंहालाँकि इस तरह की छोटी असुविधाएँ अपेक्षाकृत आसानी से हल हो सकती हैं, इसलिए issue खोलना अच्छा रहेगा
uv run mainभी चलाया जा सकता हैतब यह ज़रूरी dependencies automatically install, cache और run करेगा: https://docs.astral.sh/uv/guides/scripts/
uv tool upgradeनहीं चला सकता?ठीक यही use case है या नहीं, पता नहीं, लेकिन polyrepo microservice ecosystem को sync में रखने के लिए यह बहुत अच्छा रहा
पुरानी dependencies की सूची देखने के लिए
"uv tree --outdated --depth 1"की सिफारिश देखना काफ़ी चौंकाने वाला थाव्यक्तिगत रूप से मैं इसके आने के बाद से
"uv pip list --outdated"इस्तेमाल करता रहा हूँफिर भी मैं सहमत हूँ कि इतना महत्वपूर्ण command अलग top-level subcommand का हकदार है
"uv pip list --outdated"वाकई कहीं बेहतर output देता है, धन्यवादलेकिन पुरानी packages देखने के दो तरीके होना और दोनों के output का बहुत अलग होना भी UX को खराब बताने का कारण लगता है
"uv tree -od1"भी शायद काम करेगालेकिन pacman जैसे package managers की आलोचना में भी यही बात थी कि apt की तरह अक्सर इस्तेमाल होने वाले commands के लिए इंसान को समझ आने वाले command names देने चाहिए
शीर्षक में “mess” जैसी अभिव्यक्ति की तुलना में, उदाहरण तो बस कुछ extra arguments लिखने की बात हैं
बेहतर शीर्षक शायद UV में जिन quality-of-life सुधारों की इच्छा है जैसा होता
feedback खुद उपयोगी है और ज्यादातर बातों से सहमति है, लेकिन ऐसी भाषा feedback की value घटाती है और defensive reactions बुलाती है
uv का command line interface मुझे भी व्यक्तिगत रूप से awkward लगता है, लेकिन यह इस तरह क्यों लिखा गया, यह समझ में आता है
uvशानदार है, लेकिन मौजूदा Python packaging की सबसे बड़ी समस्या अब भी scientific/machine learning packaging को सही ढंग से संभालना हैअगर PyTorch install करना है, तो पहले यह देखना पड़ता है कि कौन सा version चाहिए, CUDA चाहिए या नहीं। CUDA चाहिए तो CUDA version के हिसाब से 6 तरह के versions हैं, और wheels इतने बड़े हैं कि उन्हें PyPI पर डालना मुश्किल है, इसलिए अलग से लेना पड़ता है
Conda इस समस्या को सिर्फ आंशिक रूप से हल करता है। Spack बेहद configurable है और ज़रूरी C/C++/Fortran dependencies तथा compiler toolchain तक उपलब्ध करा सकता है, इसलिए सर्वोत्तम performance निकालने के लिए बेहतरीन है, लेकिन uv आदि के साथ अच्छी तरह integrate नहीं होता। इसलिए researchers द्वारा बनाए गए experimental machine learning projects को production तक ले जाना मुश्किल हो जाता है
आख़िरकार फिर वही पहले बताई गई स्थिति लौट आती है
इसमें काफ़ी उपयोगी feedback है, लेकिन clickbait जैसी भाषा भी मिली हुई है
pnpm outdatedके बारे में यह बात अब तक ज़्यादा सामने नहीं आई, लेकिन यह एक reasonable मांग लगती है। शायद यह Python और JavaScript संस्कृति के अंतर से आता है। Python dependencies vulnerable या broken न हों तो वे outdated हैं या नहीं, इस पर मैंने लगभग कभी ध्यान नहीं दिया, लेकिन JavaScript ecosystem में मौका मिले तो upgrade करना काफ़ी आम लगता है। यह बुरी बात नहीं है, बल्कि यह दिखाता है कि बड़े programming communities के बीच command line interface में क्या surface किया जाना चाहिए, इस बारे में intuition कितनी अलग हो सकती हैजैसा Armin ने कहा, uv का upper bounds वाला behavior जानबूझकर है, और Python resolution model के हिसाब से यह functional necessity है। यह Python का दूसरे languages की तुलना में चुना गया trade-off है, लेकिन dependency tree में हर dependency की केवल एक copy होने और उससे जुड़ी सभी requirements का उसी पर resolve होना, मेरे हिसाब से अच्छा trade-off है
uv lock --upgradeऐसा इसलिए है क्योंकि यह user requirements नहीं बल्कि lock file को upgrade करता है। इसके विपरीतpnpm updateशायदpackage.jsonमें user requirements को update करता है। इससे भ्रम हो सकता है, लेकिन इसेuv lockके तहत रखना ज़्यादा सही है। वरनाuv upgradeनाम देखकर users उलझेंगे कि यह उनके हिसाब का upgrade क्यों नहीं कर रहा। फिर भी इसे और साफ़ तरीके से पेश करने की गुंजाइश है, और requirements को सीधे upgrade करने वाले uv subcommand की user demand स्पष्ट रूप से रही हैhttps://news.ycombinator.com/item?id=48230048
uv lockcommand सिर्फ lock file को संभालता है, यह समझ में आता है, लेकिन users को direct और transitive dependencies upgrade करने की वास्तविक ज़रूरत होती है। transitive dependenciesuv lock --upgrade-packageसे हो सकती हैं, लेकिन यह थोड़ा verbose है। direct dependencies पर भी यह काम करता है, लेकिनpyproject.tomlको नहीं छूता, जबकि developers के लिए वही file कहीं ज़्यादा नज़र में रहती हैअगर
uv.lockमें package versionspyproject.tomlसे आगे निकल जाएँ, तोpyproject.tomldependency surface समझने के guide के रूप में कम भरोसेमंद हो जाता है। एक friendlyuv upgradecommand अच्छा रहेगाअब तक देखे गए uv UX का सबसे बड़ा trap
uv pipहै। बहुत से projects development मेंpyproject.tomlऔरuv.lockके साथ uv का सही उपयोग करते हैं, लेकिन deployment Dockerfile या CI tooling मेंuv pip install -r pyproject.tomlलिखकरuv.lockको bypass कर देते हैंcoding agents भी training data में
pipबहुत ज़्यादा होने की वजह से खराबuv pippatterns सुझाते हैं, यह भी समस्या है, लेकिन uv को भी users की सुरक्षा के लिए guardrails देने चाहिएuv शानदार tool है और इसका ज़्यादा उपयोग होना चाहिए: https://aleyan.com/blog/2026-why-arent-we-uv-yet
poetry updateभी lock file update करता है। uv CLI का संगठन मुझे काम करने में काफ़ी परेशान करने वाला लगता है। ऐसा महसूस होता है कि इसे correctness और machines के लिए design किया गया है, user-friendliness के लिए नहींuv pip list --outdatedपर वापस चले जाते हैंuv upgraderoadmap पर हैअभी तक नहीं हुआ क्योंकि इसे शानदार अनुभव बनाना मुश्किल है, इसमें लोगों की अपेक्षा से कहीं ज़्यादा nuance है, और टीम छोटी है जबकि priorities बहुत हैं
pnpm outdatedजैसे feature का एक उपयोग यह देखना है कि"uv sync --update"या"uv lock --update"चलाने पर क्या update होगाहालाँकि शायद उन commands में confirmation prompt देना बेहतर हो सकता है
Pixi, uv को backend के रूप में इस्तेमाल करता है, और outdated packages को अच्छे ढंग से list करने वाला task alias आसानी से जोड़ने देता है, इसलिए उसका UI पसंद आया
खासकर Pixi-diff-to-markdown automatic CI package updates को जल्दी समझने में मदद करता है
अगर आप outdated packages में से यह देखना चाहते हैं कि क्या update होगा, तो project में ऐसा task alias बनाया जा सकता है
pixi task add outdated "pixi update --dry-run --json | pixi exec pixi-diff-to-markdown"और फिर project में
pixi run outdatedचलाइएoutput एक पढ़ने में आसान Markdown table होता है जिसमें update होने वाले packages, पुराना version, और
pixi updatecommand से install होने वाला नया version दिखता है। बेशक, यह पसंद और परिस्थिति पर निर्भर कर सकता हैहाल ही में
envscript path में आ गई और सामान्य UNIXenvcommand के उपयोग में बाधा डालने लगीबाद में पता चला कि uv installer नीचे वाला command चलाने पर इसे बनाता है
curl -LsSf [https://astral.sh/uv/install.sh](<https://astral.sh/uv/install.sh>) | shयह
$HOME/.cargo/bin/में install करता है, और$HOME/.cargo/envमें shell script बनाता है जो$HOME/.cargo/bin/अगरPATHमें न हो तो उसे आगे जोड़ देती हैयह समझना मुश्किल है कि कौन से programmers ऐसा code लिखते हैं जो इस तरह बुनियादी UNIX commands को overwrite कर दे