Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions morgan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from morgan import configurator, metadata, server
from morgan.__about__ import __version__
from morgan.utils import Cache, to_single_dash, touch_file
from morgan.utils import Cache, ListExtendingOrderedDict, to_single_dash, touch_file

PYPI_ADDRESS = "https://pypi.org/simple/"
PREFERRED_HASH_ALG = "sha256"
Expand All @@ -44,9 +44,11 @@ def __init__(self, args: argparse.Namespace):
self.index_path = args.index_path
self.index_url = args.index_url
self.mirror_all_versions: bool = args.mirror_all_versions
self.package_type_regex: str = args.package_type_regex
self.config = configparser.ConfigParser()
self.config = configparser.ConfigParser(
strict=False, dict_type=ListExtendingOrderedDict
)
self.config.read(args.config)
self.package_type_regex: str = args.package_type_regex
self.envs = {}
self._supported_pyversions = []
self._supported_platforms = []
Expand Down
37 changes: 37 additions & 0 deletions morgan/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import re
from collections import OrderedDict

import dateutil # type: ignore[import-untyped]
from packaging.requirements import Requirement
Expand Down Expand Up @@ -46,6 +47,42 @@ def is_simple_case(self, req):
return False


class ListExtendingOrderedDict(OrderedDict):
"""An OrderedDict subclass that aggregates list values for duplicate keys.

This class extends OrderedDict to provide special handling for list values.
When a list value is assigned to an existing key, the new list is extended
onto the existing list instead of replacing it.

In the context of configparser, this allows for accumulating multiple values
from different sections or repeated keys, such as in multiline requirements.

Examples:
>>> d = MultiOrderedDict()
>>> d['key'] = [1, 2]
>>> d['key'] = [3, 4]
>>> d['key']
[1, 2, 3, 4]
>>> d['other'] = 'value'
>>> d['other'] = 'new_value' # Non-list values behave normally
>>> d['other']
'new_value'
"""

def __setitem__(self, key, value):
"""Sets the value for the given key, extending lists if the key exists.

Args:
key: The dictionary key.
value: The value to set. If this is a list and the key already exists,
the list will be extended to the existing value instead of replacing it.
"""
if isinstance(value, list) and key in self:
self[key].extend(value)
else:
super().__setitem__(key, value)


def touch_file(path: str, fileinfo: dict):
'upload-time: 2025-05-28T18:46:29.349478Z'
time_str = fileinfo.get('upload-time')
Expand Down
Loading