pipu-cli 0.1.dev6__py3-none-any.whl → 0.1.dev7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pipu_cli/__init__.py +1 -1
- pipu_cli/cli.py +12 -7
- pipu_cli/internals.py +8 -12
- pipu_cli/package_constraints.py +70 -60
- pipu_cli/ui/apps.py +12 -8
- pipu_cli/ui/constants.py +14 -0
- pipu_cli/ui/modal_dialogs.py +1 -1
- pipu_cli/ui/table_widgets.py +4 -5
- {pipu_cli-0.1.dev6.dist-info → pipu_cli-0.1.dev7.dist-info}/METADATA +1 -1
- pipu_cli-0.1.dev7.dist-info/RECORD +19 -0
- pipu_cli-0.1.dev6.dist-info/RECORD +0 -19
- {pipu_cli-0.1.dev6.dist-info → pipu_cli-0.1.dev7.dist-info}/WHEEL +0 -0
- {pipu_cli-0.1.dev6.dist-info → pipu_cli-0.1.dev7.dist-info}/entry_points.txt +0 -0
- {pipu_cli-0.1.dev6.dist-info → pipu_cli-0.1.dev7.dist-info}/licenses/LICENSE +0 -0
- {pipu_cli-0.1.dev6.dist-info → pipu_cli-0.1.dev7.dist-info}/top_level.txt +0 -0
pipu_cli/__init__.py
CHANGED
pipu_cli/cli.py
CHANGED
|
@@ -2,12 +2,18 @@ import sys
|
|
|
2
2
|
from typing import List
|
|
3
3
|
import rich_click as click
|
|
4
4
|
from .internals import list_outdated
|
|
5
|
-
from .package_constraints import
|
|
5
|
+
from .package_constraints import (
|
|
6
|
+
read_constraints,
|
|
7
|
+
read_ignores,
|
|
8
|
+
read_invalidation_triggers,
|
|
9
|
+
_get_constraint_invalid_when,
|
|
10
|
+
_set_constraint_invalid_when
|
|
11
|
+
)
|
|
6
12
|
from .common import console
|
|
7
13
|
from pip._internal.commands.install import InstallCommand
|
|
8
14
|
|
|
9
15
|
|
|
10
|
-
def _install_packages(package_names: List[str], packages_being_updated: List[str] = None) -> int:
|
|
16
|
+
def _install_packages(package_names: List[str], packages_being_updated: List[str] | None = None) -> int:
|
|
11
17
|
"""
|
|
12
18
|
Install packages using pip API with filtered constraints.
|
|
13
19
|
|
|
@@ -553,8 +559,8 @@ def constrain(constraint_specs, env, list_constraints, remove_constraints, remov
|
|
|
553
559
|
|
|
554
560
|
# Get existing invalidation triggers
|
|
555
561
|
existing_triggers_storage = {}
|
|
556
|
-
|
|
557
|
-
|
|
562
|
+
existing_value = _get_constraint_invalid_when(config, section_name)
|
|
563
|
+
if existing_value:
|
|
558
564
|
existing_triggers_storage = parse_invalidation_triggers_storage(existing_value)
|
|
559
565
|
|
|
560
566
|
# Get current constraints to find the packages that were added/updated
|
|
@@ -595,9 +601,8 @@ def constrain(constraint_specs, env, list_constraints, remove_constraints, remov
|
|
|
595
601
|
if formatted_entry:
|
|
596
602
|
trigger_entries.append(formatted_entry)
|
|
597
603
|
|
|
598
|
-
if trigger_entries
|
|
599
|
-
|
|
600
|
-
config.set(section_name, 'constraint_invalid_when', triggers_value)
|
|
604
|
+
triggers_value = ','.join(trigger_entries) if trigger_entries else ''
|
|
605
|
+
_set_constraint_invalid_when(config, section_name, triggers_value)
|
|
601
606
|
|
|
602
607
|
# Write the updated config file
|
|
603
608
|
with open(config_path, 'w', encoding='utf-8') as f:
|
pipu_cli/internals.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
import threading
|
|
1
4
|
from pip._internal.metadata import get_default_environment
|
|
2
5
|
from pip._internal.index.package_finder import PackageFinder
|
|
3
6
|
from pip._internal.index.collector import LinkCollector
|
|
@@ -10,10 +13,8 @@ from rich.table import Table
|
|
|
10
13
|
from typing import List, Dict, Any, Optional, Union, Set, Callable, Tuple
|
|
11
14
|
from packaging.version import Version, InvalidVersion
|
|
12
15
|
from packaging.specifiers import SpecifierSet, InvalidSpecifier
|
|
13
|
-
import logging
|
|
14
|
-
import time
|
|
15
|
-
import threading
|
|
16
16
|
from queue import Queue, Empty
|
|
17
|
+
from .config import EDITABLE_PACKAGES_CACHE_TTL
|
|
17
18
|
|
|
18
19
|
# Import configuration
|
|
19
20
|
from .config import (
|
|
@@ -329,7 +330,6 @@ def list_outdated(
|
|
|
329
330
|
|
|
330
331
|
# Find the best candidate (latest version) with retry logic
|
|
331
332
|
candidates = None
|
|
332
|
-
last_error = None
|
|
333
333
|
for attempt in range(retries + 1):
|
|
334
334
|
try:
|
|
335
335
|
# Use hard timeout wrapper to ensure we don't hang
|
|
@@ -345,7 +345,6 @@ def list_outdated(
|
|
|
345
345
|
break
|
|
346
346
|
except (TimeoutError, Exception) as e:
|
|
347
347
|
logger.debug(f"Error checking {package_name}: {type(e).__name__}: {e}")
|
|
348
|
-
last_error = e
|
|
349
348
|
# Check if it's a network-related error (TimeoutError always is)
|
|
350
349
|
error_str = str(e).lower()
|
|
351
350
|
is_network_error = isinstance(e, TimeoutError) or any(keyword in error_str for keyword in [
|
|
@@ -394,7 +393,7 @@ def list_outdated(
|
|
|
394
393
|
candidates_to_check = stable_candidates if stable_candidates else candidates
|
|
395
394
|
else:
|
|
396
395
|
candidates_to_check = candidates
|
|
397
|
-
|
|
396
|
+
|
|
398
397
|
# Get the latest version from filtered candidates
|
|
399
398
|
if candidates_to_check:
|
|
400
399
|
latest_candidate = max(candidates_to_check, key=lambda c: c.version)
|
|
@@ -486,7 +485,7 @@ def list_outdated(
|
|
|
486
485
|
table.add_column("Latest", no_wrap=True) # Color conditionally per row
|
|
487
486
|
table.add_column("Type", style="yellow")
|
|
488
487
|
table.add_column("Constraint", no_wrap=True)
|
|
489
|
-
table.add_column("Invalid When", no_wrap=True)
|
|
488
|
+
table.add_column("Constraint Invalid When", no_wrap=True)
|
|
490
489
|
|
|
491
490
|
for package in outdated_packages:
|
|
492
491
|
constraint = package.get("constraint")
|
|
@@ -535,7 +534,7 @@ def list_outdated(
|
|
|
535
534
|
|
|
536
535
|
|
|
537
536
|
# Thread-safe cache for editable packages to avoid repeated subprocess calls
|
|
538
|
-
|
|
537
|
+
|
|
539
538
|
|
|
540
539
|
# Initialize thread-safe cache
|
|
541
540
|
_editable_packages_cache = ThreadSafeCache[Dict[str, str]](ttl=EDITABLE_PACKAGES_CACHE_TTL)
|
|
@@ -670,7 +669,6 @@ def update_packages_preserving_editable(
|
|
|
670
669
|
import tempfile
|
|
671
670
|
import os
|
|
672
671
|
from packaging.utils import canonicalize_name
|
|
673
|
-
from pathlib import Path
|
|
674
672
|
|
|
675
673
|
if console is None:
|
|
676
674
|
console = Console()
|
|
@@ -702,7 +700,6 @@ def update_packages_preserving_editable(
|
|
|
702
700
|
for pkg, constraint in filtered_constraints.items():
|
|
703
701
|
constraint_file.write(f"{pkg}{constraint}\n")
|
|
704
702
|
constraint_file.close()
|
|
705
|
-
console.print(f"[dim]Using filtered constraints (excluding {len(packages_being_updated)} package(s) being updated)[/dim]")
|
|
706
703
|
|
|
707
704
|
# Helper function to run subprocess with cancellation support
|
|
708
705
|
def run_with_cancel(cmd, timeout=None):
|
|
@@ -733,7 +730,7 @@ def update_packages_preserving_editable(
|
|
|
733
730
|
process.kill()
|
|
734
731
|
try:
|
|
735
732
|
process.communicate(timeout=1)
|
|
736
|
-
except:
|
|
733
|
+
except Exception:
|
|
737
734
|
pass
|
|
738
735
|
raise
|
|
739
736
|
|
|
@@ -747,7 +744,6 @@ def update_packages_preserving_editable(
|
|
|
747
744
|
break
|
|
748
745
|
|
|
749
746
|
package_name = package_info["name"]
|
|
750
|
-
latest_version = package_info.get("latest_version")
|
|
751
747
|
is_editable = package_info.get("editable", False)
|
|
752
748
|
|
|
753
749
|
try:
|
pipu_cli/package_constraints.py
CHANGED
|
@@ -155,22 +155,21 @@ def validate_existing_constraints_and_triggers(env_name: Optional[str] = None) -
|
|
|
155
155
|
invalid_constraint_packages.append(package_name)
|
|
156
156
|
|
|
157
157
|
# Check invalidation trigger packages
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
invalid_triggers_for_package.append(trigger_package)
|
|
158
|
+
triggers_value = _get_constraint_invalid_when(config, section_name)
|
|
159
|
+
if triggers_value and triggers_value.strip():
|
|
160
|
+
all_triggers = parse_invalidation_triggers_storage(triggers_value)
|
|
161
|
+
for constrained_package, triggers in all_triggers.items():
|
|
162
|
+
invalid_triggers_for_package = []
|
|
163
|
+
for trigger in triggers:
|
|
164
|
+
parsed = parse_invalidation_trigger(trigger)
|
|
165
|
+
if parsed:
|
|
166
|
+
from packaging.utils import canonicalize_name
|
|
167
|
+
trigger_package = canonicalize_name(parsed['name'])
|
|
168
|
+
if trigger_package not in installed_packages:
|
|
169
|
+
invalid_triggers_for_package.append(trigger_package)
|
|
171
170
|
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
if invalid_triggers_for_package:
|
|
172
|
+
invalid_trigger_packages[constrained_package] = invalid_triggers_for_package
|
|
174
173
|
|
|
175
174
|
except (OSError, ValueError, KeyError) as e:
|
|
176
175
|
# If validation fails, log and return empty results to avoid disrupting the app
|
|
@@ -209,8 +208,10 @@ def cleanup_invalid_constraints_and_triggers(env_name: Optional[str] = None) ->
|
|
|
209
208
|
section_name = _get_section_name(env_name)
|
|
210
209
|
config, config_path = _load_config(create_if_missing=False)
|
|
211
210
|
|
|
212
|
-
if config.has_section(section_name)
|
|
213
|
-
triggers_value = config
|
|
211
|
+
if config.has_section(section_name):
|
|
212
|
+
triggers_value = _get_constraint_invalid_when(config, section_name)
|
|
213
|
+
if not triggers_value:
|
|
214
|
+
return
|
|
214
215
|
all_triggers = parse_invalidation_triggers_storage(triggers_value)
|
|
215
216
|
|
|
216
217
|
# Remove invalid triggers while keeping valid ones
|
|
@@ -249,16 +250,12 @@ def cleanup_invalid_constraints_and_triggers(env_name: Optional[str] = None) ->
|
|
|
249
250
|
if formatted_entry:
|
|
250
251
|
trigger_entries.append(formatted_entry)
|
|
251
252
|
|
|
252
|
-
if trigger_entries
|
|
253
|
-
|
|
254
|
-
config.set(section_name, 'constraint_invalid_when', new_triggers_value)
|
|
255
|
-
else:
|
|
256
|
-
config.remove_option(section_name, 'constraint_invalid_when')
|
|
257
|
-
|
|
253
|
+
new_triggers_value = ','.join(trigger_entries) if trigger_entries else ''
|
|
254
|
+
_set_constraint_invalid_when(config, section_name, new_triggers_value)
|
|
258
255
|
_write_config_file(config, config_path)
|
|
259
256
|
else:
|
|
260
257
|
# No valid triggers left, remove the option
|
|
261
|
-
config
|
|
258
|
+
_set_constraint_invalid_when(config, section_name, '')
|
|
262
259
|
_write_config_file(config, config_path)
|
|
263
260
|
|
|
264
261
|
except (OSError, configparser.Error) as e:
|
|
@@ -324,6 +321,33 @@ def _load_config(create_if_missing: bool = False) -> Tuple[configparser.ConfigPa
|
|
|
324
321
|
return config, config_path
|
|
325
322
|
|
|
326
323
|
|
|
324
|
+
def _get_constraint_invalid_when(config: configparser.ConfigParser, section_name: str) -> Optional[str]:
|
|
325
|
+
"""
|
|
326
|
+
Get constraint_invalid_when value from config with consistent pattern.
|
|
327
|
+
|
|
328
|
+
:param config: ConfigParser instance
|
|
329
|
+
:param section_name: Name of section to read from
|
|
330
|
+
:returns: Constraint invalid when value or None if not present
|
|
331
|
+
"""
|
|
332
|
+
if config.has_option(section_name, 'constraint_invalid_when'):
|
|
333
|
+
return config.get(section_name, 'constraint_invalid_when')
|
|
334
|
+
return None
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def _set_constraint_invalid_when(config: configparser.ConfigParser, section_name: str, value: str) -> None:
|
|
338
|
+
"""
|
|
339
|
+
Set constraint_invalid_when value in config.
|
|
340
|
+
|
|
341
|
+
:param config: ConfigParser instance
|
|
342
|
+
:param section_name: Name of section to write to
|
|
343
|
+
:param value: Value to set (will remove option if empty)
|
|
344
|
+
"""
|
|
345
|
+
if value and value.strip():
|
|
346
|
+
config.set(section_name, 'constraint_invalid_when', value)
|
|
347
|
+
elif config.has_option(section_name, 'constraint_invalid_when'):
|
|
348
|
+
config.remove_option(section_name, 'constraint_invalid_when')
|
|
349
|
+
|
|
350
|
+
|
|
327
351
|
def _ensure_section_exists(config: configparser.ConfigParser, section_name: str) -> None:
|
|
328
352
|
"""
|
|
329
353
|
Ensure a config section exists, creating it if necessary.
|
|
@@ -385,11 +409,8 @@ def _cleanup_invalidation_triggers(config: configparser.ConfigParser, section_na
|
|
|
385
409
|
"""
|
|
386
410
|
removed_triggers = {}
|
|
387
411
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
triggers_value = config.get(section_name, 'constraint_invalid_when')
|
|
392
|
-
if not triggers_value.strip():
|
|
412
|
+
triggers_value = _get_constraint_invalid_when(config, section_name)
|
|
413
|
+
if not triggers_value or not triggers_value.strip():
|
|
393
414
|
return removed_triggers
|
|
394
415
|
|
|
395
416
|
# Parse existing triggers
|
|
@@ -416,13 +437,10 @@ def _cleanup_invalidation_triggers(config: configparser.ConfigParser, section_na
|
|
|
416
437
|
if formatted_entry:
|
|
417
438
|
trigger_entries.append(formatted_entry)
|
|
418
439
|
|
|
419
|
-
if trigger_entries
|
|
420
|
-
new_triggers_value = ','.join(trigger_entries)
|
|
421
|
-
config.set(section_name, 'constraint_invalid_when', new_triggers_value)
|
|
422
|
-
else:
|
|
423
|
-
config.remove_option(section_name, 'constraint_invalid_when')
|
|
440
|
+
new_triggers_value = ','.join(trigger_entries) if trigger_entries else ''
|
|
424
441
|
else:
|
|
425
|
-
|
|
442
|
+
new_triggers_value = ''
|
|
443
|
+
_set_constraint_invalid_when(config, section_name, new_triggers_value)
|
|
426
444
|
|
|
427
445
|
return removed_triggers
|
|
428
446
|
|
|
@@ -1158,13 +1176,12 @@ def remove_all_constraints_from_config(env_name: Optional[str] = None) -> Tuple[
|
|
|
1158
1176
|
removed_constraints[environment] = existing_constraints
|
|
1159
1177
|
|
|
1160
1178
|
# Clean up all invalidation triggers for this environment and capture what was removed
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
config.remove_option(environment, 'constraint_invalid_when')
|
|
1179
|
+
triggers_value = _get_constraint_invalid_when(config, environment)
|
|
1180
|
+
if triggers_value and triggers_value.strip():
|
|
1181
|
+
existing_triggers = parse_invalidation_triggers_storage(triggers_value)
|
|
1182
|
+
if existing_triggers:
|
|
1183
|
+
removed_triggers[environment] = existing_triggers
|
|
1184
|
+
_set_constraint_invalid_when(config, environment, '')
|
|
1168
1185
|
|
|
1169
1186
|
# Remove the constraints option
|
|
1170
1187
|
config.remove_option(environment, 'constraints')
|
|
@@ -1525,9 +1542,9 @@ def read_invalidation_triggers() -> Dict[str, List[str]]:
|
|
|
1525
1542
|
section_name = _get_section_name(None)
|
|
1526
1543
|
config, _ = _load_config(create_if_missing=False)
|
|
1527
1544
|
|
|
1528
|
-
if config.has_section(section_name)
|
|
1529
|
-
triggers_value = config
|
|
1530
|
-
if triggers_value.strip():
|
|
1545
|
+
if config.has_section(section_name):
|
|
1546
|
+
triggers_value = _get_constraint_invalid_when(config, section_name)
|
|
1547
|
+
if triggers_value and triggers_value.strip():
|
|
1531
1548
|
triggers_map = parse_invalidation_triggers_storage(triggers_value)
|
|
1532
1549
|
|
|
1533
1550
|
except Exception:
|
|
@@ -1942,8 +1959,8 @@ def apply_auto_constraints(env_name: Optional[str] = None, dry_run: bool = False
|
|
|
1942
1959
|
|
|
1943
1960
|
# Get existing triggers
|
|
1944
1961
|
existing_triggers_storage = {}
|
|
1945
|
-
|
|
1946
|
-
|
|
1962
|
+
existing_value = _get_constraint_invalid_when(config, section_name)
|
|
1963
|
+
if existing_value:
|
|
1947
1964
|
existing_triggers_storage = parse_invalidation_triggers_storage(existing_value)
|
|
1948
1965
|
|
|
1949
1966
|
# Get current constraints for formatting
|
|
@@ -1978,9 +1995,8 @@ def apply_auto_constraints(env_name: Optional[str] = None, dry_run: bool = False
|
|
|
1978
1995
|
if formatted_entry:
|
|
1979
1996
|
trigger_entries.append(formatted_entry)
|
|
1980
1997
|
|
|
1981
|
-
if trigger_entries
|
|
1982
|
-
|
|
1983
|
-
config.set(section_name, 'constraint_invalid_when', triggers_value)
|
|
1998
|
+
triggers_value = ','.join(trigger_entries) if trigger_entries else ''
|
|
1999
|
+
_set_constraint_invalid_when(config, section_name, triggers_value)
|
|
1984
2000
|
|
|
1985
2001
|
# Write the updated config file
|
|
1986
2002
|
_write_config_file(config, config_path)
|
|
@@ -2019,11 +2035,8 @@ def check_constraint_invalidations(packages_to_install: List[str], env_name: Opt
|
|
|
2019
2035
|
current_constraints = parse_inline_constraints(constraints_value)
|
|
2020
2036
|
|
|
2021
2037
|
# Get invalidation triggers
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
triggers_value = config.get(section_name, 'constraint_invalid_when')
|
|
2026
|
-
if not triggers_value.strip():
|
|
2038
|
+
triggers_value = _get_constraint_invalid_when(config, section_name)
|
|
2039
|
+
if not triggers_value or not triggers_value.strip():
|
|
2027
2040
|
return invalidated_constraints
|
|
2028
2041
|
|
|
2029
2042
|
# Parse triggers
|
|
@@ -2150,11 +2163,8 @@ def evaluate_invalidation_triggers(env_name: Optional[str] = None) -> Tuple[List
|
|
|
2150
2163
|
current_constraints = parse_inline_constraints(constraints_value)
|
|
2151
2164
|
|
|
2152
2165
|
# Get invalidation triggers
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
triggers_value = config.get(section_name, 'constraint_invalid_when')
|
|
2157
|
-
if not triggers_value.strip():
|
|
2166
|
+
triggers_value = _get_constraint_invalid_when(config, section_name)
|
|
2167
|
+
if not triggers_value or not triggers_value.strip():
|
|
2158
2168
|
return constraints_to_remove, trigger_details
|
|
2159
2169
|
|
|
2160
2170
|
# Parse triggers
|
pipu_cli/ui/apps.py
CHANGED
|
@@ -23,7 +23,13 @@ from textual.errors import NoWidget
|
|
|
23
23
|
from textual.coordinate import Coordinate
|
|
24
24
|
from rich.text import Text
|
|
25
25
|
from ..internals import _check_constraint_satisfaction, list_outdated, get_constraint_color
|
|
26
|
-
from ..package_constraints import
|
|
26
|
+
from ..package_constraints import (
|
|
27
|
+
add_constraints_to_config,
|
|
28
|
+
read_constraints,
|
|
29
|
+
read_ignores,
|
|
30
|
+
_get_constraint_invalid_when,
|
|
31
|
+
_set_constraint_invalid_when
|
|
32
|
+
)
|
|
27
33
|
from pip._internal.metadata import get_default_environment
|
|
28
34
|
|
|
29
35
|
# Import modular components
|
|
@@ -422,7 +428,7 @@ class MainTUIApp(App):
|
|
|
422
428
|
table.add_column("Latest", width=12)
|
|
423
429
|
table.add_column("Type", width=8)
|
|
424
430
|
table.add_column("Constraint", width=20)
|
|
425
|
-
table.add_column("Invalid When", width=
|
|
431
|
+
table.add_column("Constraint Invalid When", width=30)
|
|
426
432
|
except Exception:
|
|
427
433
|
# App not mounted or table not available (e.g., during testing)
|
|
428
434
|
pass
|
|
@@ -1154,9 +1160,7 @@ class MainTUIApp(App):
|
|
|
1154
1160
|
formatted_entry = format_invalidation_triggers(constraint_spec, [invalidation_trigger])
|
|
1155
1161
|
if formatted_entry:
|
|
1156
1162
|
# Get existing triggers
|
|
1157
|
-
existing_triggers = ""
|
|
1158
|
-
if config.has_option(section_name, 'constraint_invalid_when'):
|
|
1159
|
-
existing_triggers = config.get(section_name, 'constraint_invalid_when')
|
|
1163
|
+
existing_triggers = _get_constraint_invalid_when(config, section_name) or ""
|
|
1160
1164
|
|
|
1161
1165
|
# Add the new trigger
|
|
1162
1166
|
if existing_triggers.strip():
|
|
@@ -1164,7 +1168,7 @@ class MainTUIApp(App):
|
|
|
1164
1168
|
else:
|
|
1165
1169
|
triggers_value = formatted_entry
|
|
1166
1170
|
|
|
1167
|
-
config
|
|
1171
|
+
_set_constraint_invalid_when(config, section_name, triggers_value)
|
|
1168
1172
|
_write_config_file(config, config_path)
|
|
1169
1173
|
|
|
1170
1174
|
# Update the package data in all_packages
|
|
@@ -1284,8 +1288,8 @@ class MainTUIApp(App):
|
|
|
1284
1288
|
config.remove_option(section_name, 'constraint')
|
|
1285
1289
|
options_removed.append('constraints')
|
|
1286
1290
|
|
|
1287
|
-
if config
|
|
1288
|
-
config
|
|
1291
|
+
if _get_constraint_invalid_when(config, section_name):
|
|
1292
|
+
_set_constraint_invalid_when(config, section_name, '')
|
|
1289
1293
|
options_removed.append('invalidation triggers')
|
|
1290
1294
|
|
|
1291
1295
|
# Remove the section if it's empty
|
pipu_cli/ui/constants.py
CHANGED
|
@@ -13,6 +13,20 @@ COLUMN_TYPE = 4
|
|
|
13
13
|
COLUMN_CONSTRAINT = 5
|
|
14
14
|
COLUMN_INVALID_WHEN = 6
|
|
15
15
|
|
|
16
|
+
# Table column definitions for consistent display across UI
|
|
17
|
+
# These can be used to ensure all tables have the same structure
|
|
18
|
+
TABLE_COLUMNS = {
|
|
19
|
+
"package": {"title": "Package", "width": 20},
|
|
20
|
+
"current": {"title": "Current", "width": 12},
|
|
21
|
+
"latest": {"title": "Latest", "width": 12},
|
|
22
|
+
"type": {"title": "Type", "width": 8},
|
|
23
|
+
"constraint": {"title": "Constraint", "width": 20},
|
|
24
|
+
"constraint_invalid_when": {"title": "Constraint Invalid When", "width": 30},
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Config option name
|
|
28
|
+
CONFIG_CONSTRAINT_INVALID_WHEN = 'constraint_invalid_when'
|
|
29
|
+
|
|
16
30
|
# UI timeouts and limits
|
|
17
31
|
FORCE_EXIT_TIMEOUT = 0.5 # seconds
|
|
18
32
|
UNINSTALL_TIMEOUT = 30 # seconds
|
pipu_cli/ui/modal_dialogs.py
CHANGED
|
@@ -452,7 +452,7 @@ class HelpScreen(ModalScreen):
|
|
|
452
452
|
"• Auto-discovered constraints protect dependencies automatically (no manual action needed)\n"
|
|
453
453
|
"• Review constraint colors: green = can update, red = blocked by constraint\n"
|
|
454
454
|
"• Use 'C' to add custom manual constraints for packages you want to pin\n"
|
|
455
|
-
"• Check 'Invalid When' column to understand when constraints will be removed",
|
|
455
|
+
"• Check 'Constraint Invalid When' column to understand when constraints will be removed",
|
|
456
456
|
id="tips-text"
|
|
457
457
|
)
|
|
458
458
|
|
pipu_cli/ui/table_widgets.py
CHANGED
|
@@ -12,6 +12,7 @@ from textual.message import Message
|
|
|
12
12
|
from textual.coordinate import Coordinate
|
|
13
13
|
from rich.text import Text
|
|
14
14
|
from ..internals import _check_constraint_satisfaction, get_constraint_color, format_invalid_when_display
|
|
15
|
+
from ..package_constraints import _get_constraint_invalid_when, _set_constraint_invalid_when
|
|
15
16
|
from .constants import (
|
|
16
17
|
COLUMN_SELECTION, COLUMN_CONSTRAINT, COLUMN_INVALID_WHEN
|
|
17
18
|
)
|
|
@@ -98,7 +99,7 @@ class PackageSelectionTable(DataTable):
|
|
|
98
99
|
self.add_column("Latest", width=10)
|
|
99
100
|
self.add_column("Type", width=8)
|
|
100
101
|
self.add_column("Constraint", width=20)
|
|
101
|
-
self.add_column("Invalid When", width=
|
|
102
|
+
self.add_column("Constraint Invalid When", width=30)
|
|
102
103
|
|
|
103
104
|
# Add rows
|
|
104
105
|
for pkg in self.outdated_packages:
|
|
@@ -231,9 +232,7 @@ class PackageSelectionTable(DataTable):
|
|
|
231
232
|
formatted_entry = format_invalidation_triggers(constraint_spec, [invalidation_trigger])
|
|
232
233
|
if formatted_entry:
|
|
233
234
|
# Get existing triggers
|
|
234
|
-
existing_triggers = ""
|
|
235
|
-
if config.has_option(section_name, 'constraint_invalid_when'):
|
|
236
|
-
existing_triggers = config.get(section_name, 'constraint_invalid_when')
|
|
235
|
+
existing_triggers = _get_constraint_invalid_when(config, section_name) or ""
|
|
237
236
|
|
|
238
237
|
# Add the new trigger
|
|
239
238
|
if existing_triggers.strip():
|
|
@@ -241,7 +240,7 @@ class PackageSelectionTable(DataTable):
|
|
|
241
240
|
else:
|
|
242
241
|
triggers_value = formatted_entry
|
|
243
242
|
|
|
244
|
-
config
|
|
243
|
+
_set_constraint_invalid_when(config, section_name, triggers_value)
|
|
245
244
|
_write_config_file(config, config_path)
|
|
246
245
|
|
|
247
246
|
# Update the package data and refresh display
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
pipu_cli/__init__.py,sha256=WDLP6ogP02f1a6fTLesY-8T3ezeWgpIv840DvOgPU6c,1190
|
|
2
|
+
pipu_cli/cli.py,sha256=B_cVo4iJfo1BzWhb997ehqFl71paGvqyasFQKtrDOVg,41858
|
|
3
|
+
pipu_cli/common.py,sha256=g5krfXFsvVJpOf87Vw4DGi5WIduDgMlRuONKXqO328M,78
|
|
4
|
+
pipu_cli/config.py,sha256=xsfNU4ORAujla_FGfsMKpxy7QTpd_bJhRF_u4IPKLW0,3635
|
|
5
|
+
pipu_cli/internals.py,sha256=JJFWMoEj3NPGVOIw11HKw2MdZCXkJOEZ3ckzSnrsm8s,34802
|
|
6
|
+
pipu_cli/package_constraints.py,sha256=mIuSUwEKArFHjTMstFhL21SmpTo9Td-1QGSCP7TGgOw,93258
|
|
7
|
+
pipu_cli/thread_safe.py,sha256=zdQyCoMVJW73MC-d1pL_4ZO-K4AwkI0JeVyQsd8x7nY,7545
|
|
8
|
+
pipu_cli/utils.py,sha256=ijSHKVuKbjmRbj2RwD9S1606PeY4oDiutzhutpX25wM,5842
|
|
9
|
+
pipu_cli/ui/__init__.py,sha256=nCb_3G_vZXy5_Or9z9r-3XhYV1ppUR1r7nMZ9_6Srwg,1432
|
|
10
|
+
pipu_cli/ui/apps.py,sha256=tvFtr_ZM3ZqqUzR1txWBGKfpRstTpQGyR1wULtxYfS0,59709
|
|
11
|
+
pipu_cli/ui/constants.py,sha256=aI45qJG5bknTWxUjts3sSsg0zryf5HEyfi25ugJdNMY,1001
|
|
12
|
+
pipu_cli/ui/modal_dialogs.py,sha256=ItDkb2vNJyhsmTJ659ZPDNQJOKVpP1-4asILdAKxgxw,47985
|
|
13
|
+
pipu_cli/ui/table_widgets.py,sha256=gVbzhWd1MusRYK5GLTJwUjRa3L8GAIhJOXRKDcnbeng,14276
|
|
14
|
+
pipu_cli-0.1.dev7.dist-info/licenses/LICENSE,sha256=q6TxVbSI0WMB9ulF2V0FWQfeA5om3d-T9X7QwuhdiYE,1075
|
|
15
|
+
pipu_cli-0.1.dev7.dist-info/METADATA,sha256=IUbyV2eLdnbG8rD7Vzp3NTDR1LfBVU8cqY0I1P-fFQI,13200
|
|
16
|
+
pipu_cli-0.1.dev7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
17
|
+
pipu_cli-0.1.dev7.dist-info/entry_points.txt,sha256=VSv6od00zOPblnFPflNLaci4jBtQIgLYJjL1BKxLz_o,42
|
|
18
|
+
pipu_cli-0.1.dev7.dist-info/top_level.txt,sha256=z3Yce93-jGQjGRpsGZUZvbS8osh3OyS7MVpzG0uBE5M,9
|
|
19
|
+
pipu_cli-0.1.dev7.dist-info/RECORD,,
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
pipu_cli/__init__.py,sha256=6tnAKZMPjf830m0TO279lIHtF7_B5gnWQTiYCidkeYU,1190
|
|
2
|
-
pipu_cli/cli.py,sha256=4pZh9kh0JO8FRbDBewVUJWt8iQrEC5w4AylOghYWkd4,41838
|
|
3
|
-
pipu_cli/common.py,sha256=g5krfXFsvVJpOf87Vw4DGi5WIduDgMlRuONKXqO328M,78
|
|
4
|
-
pipu_cli/config.py,sha256=xsfNU4ORAujla_FGfsMKpxy7QTpd_bJhRF_u4IPKLW0,3635
|
|
5
|
-
pipu_cli/internals.py,sha256=m_aF-yrOT5UJt54vv6X2gEukgokFXkz0m9SAKRofir0,35101
|
|
6
|
-
pipu_cli/package_constraints.py,sha256=6LSFlC93HNIFym7dYkfYn0fsOic6QDe1ADyghK94Pk8,93052
|
|
7
|
-
pipu_cli/thread_safe.py,sha256=zdQyCoMVJW73MC-d1pL_4ZO-K4AwkI0JeVyQsd8x7nY,7545
|
|
8
|
-
pipu_cli/utils.py,sha256=ijSHKVuKbjmRbj2RwD9S1606PeY4oDiutzhutpX25wM,5842
|
|
9
|
-
pipu_cli/ui/__init__.py,sha256=nCb_3G_vZXy5_Or9z9r-3XhYV1ppUR1r7nMZ9_6Srwg,1432
|
|
10
|
-
pipu_cli/ui/apps.py,sha256=ltH24sg-3nqVpoomgCwhVYuAwq3hBUwYRH60JXtV2Yg,59771
|
|
11
|
-
pipu_cli/ui/constants.py,sha256=HBPf4KYWHiT18c7ciQ0HeI7gZE3VIFOT0uobLU8YxQA,445
|
|
12
|
-
pipu_cli/ui/modal_dialogs.py,sha256=-Trh60n2mev6Pr-aIvetJHnXqqoJaVDmA3c8xH_xePM,47974
|
|
13
|
-
pipu_cli/ui/table_widgets.py,sha256=PC0CqqrK3KgMbTCYRPG01lxkEtRBz9xr9PN8EzoObz8,14322
|
|
14
|
-
pipu_cli-0.1.dev6.dist-info/licenses/LICENSE,sha256=q6TxVbSI0WMB9ulF2V0FWQfeA5om3d-T9X7QwuhdiYE,1075
|
|
15
|
-
pipu_cli-0.1.dev6.dist-info/METADATA,sha256=JqoGCM76nI14oiMrjwdvm2HjNRbVWQagOuxa_Q5ybsM,13200
|
|
16
|
-
pipu_cli-0.1.dev6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
17
|
-
pipu_cli-0.1.dev6.dist-info/entry_points.txt,sha256=VSv6od00zOPblnFPflNLaci4jBtQIgLYJjL1BKxLz_o,42
|
|
18
|
-
pipu_cli-0.1.dev6.dist-info/top_level.txt,sha256=z3Yce93-jGQjGRpsGZUZvbS8osh3OyS7MVpzG0uBE5M,9
|
|
19
|
-
pipu_cli-0.1.dev6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|