debian._deb822_repro package¶
Submodules¶
- debian._deb822_repro._util module
- debian._deb822_repro.formatter module
FormatterContentToken
FormatterContentToken._content_type
FormatterContentToken._text
FormatterContentToken.comment_token()
FormatterContentToken.from_token_or_element()
FormatterContentToken.is_comment
FormatterContentToken.is_separator
FormatterContentToken.is_value
FormatterContentToken.is_whitespace
FormatterContentToken.separator_token()
FormatterContentToken.text
FormatterContentToken.value_token()
format_field()
one_value_per_line_formatter()
one_value_per_line_trailing_separator()
- debian._deb822_repro.locatable module
Locatable
Position
Range
Range.as_size()
Range.between()
Range.end_cursor_position
Range.end_line_number
Range.end_line_position
Range.end_pos
Range.from_position_and_size()
Range.from_position_and_sizes()
Range.line_count
Range.relative_to()
Range.start_cursor_position
Range.start_line_number
Range.start_line_position
Range.start_pos
- debian._deb822_repro.parsing module
AbstractDeb822ParagraphWrapper
AutoResolvingMixin
Deb822CommentElement
Deb822DictishParagraphWrapper
Deb822DuplicateFieldsParagraphElement
Deb822DuplicateFieldsParagraphElement._find_node_via_name_token()
Deb822DuplicateFieldsParagraphElement._full_size_cache
Deb822DuplicateFieldsParagraphElement._init_kvpair_fields()
Deb822DuplicateFieldsParagraphElement._nodes_being_relocated()
Deb822DuplicateFieldsParagraphElement._parent_element
Deb822DuplicateFieldsParagraphElement._regenerate_relative_kvapir_order()
Deb822DuplicateFieldsParagraphElement._resolve_to_single_node()
Deb822DuplicateFieldsParagraphElement.contains_kvpair_element()
Deb822DuplicateFieldsParagraphElement.get_kvpair_element()
Deb822DuplicateFieldsParagraphElement.has_duplicate_fields
Deb822DuplicateFieldsParagraphElement.iter_keys()
Deb822DuplicateFieldsParagraphElement.iter_parts()
Deb822DuplicateFieldsParagraphElement.kvpair_count
Deb822DuplicateFieldsParagraphElement.order_after()
Deb822DuplicateFieldsParagraphElement.order_before()
Deb822DuplicateFieldsParagraphElement.order_first()
Deb822DuplicateFieldsParagraphElement.order_last()
Deb822DuplicateFieldsParagraphElement.remove_kvpair_element()
Deb822DuplicateFieldsParagraphElement.set_kvpair_element()
Deb822DuplicateFieldsParagraphElement.sort_fields()
Deb822Element
Deb822Element._full_size_cache
Deb822Element._init_parent_of_parts()
Deb822Element._parent_element
Deb822Element.clear_parent_if_parent()
Deb822Element.convert_to_text()
Deb822Element.is_comment
Deb822Element.is_error
Deb822Element.is_separator
Deb822Element.is_whitespace
Deb822Element.iter_parts()
Deb822Element.iter_parts_of_type()
Deb822Element.iter_recurse()
Deb822Element.iter_tokens()
Deb822Element.parent_element
Deb822Element.size()
Deb822ErrorElement
Deb822FileElement
Deb822FileElement._full_size_cache
Deb822FileElement._parent_element
Deb822FileElement._set_parent()
Deb822FileElement.append()
Deb822FileElement.dump()
Deb822FileElement.find_first_error_element()
Deb822FileElement.insert()
Deb822FileElement.is_valid_file
Deb822FileElement.iter_parts()
Deb822FileElement.new_empty_file()
Deb822FileElement.position_in_file()
Deb822FileElement.position_in_parent()
Deb822FileElement.remove()
Deb822InterpretationProxyElement
Deb822InterpretingParagraphWrapper
Deb822KeyValuePairElement
Deb822KeyValuePairElement._comment_element
Deb822KeyValuePairElement._field_token
Deb822KeyValuePairElement._separator_token
Deb822KeyValuePairElement._value_element
Deb822KeyValuePairElement.comment_element
Deb822KeyValuePairElement.field_name
Deb822KeyValuePairElement.field_token
Deb822KeyValuePairElement.interpret_as()
Deb822KeyValuePairElement.iter_parts()
Deb822KeyValuePairElement.value_element
Deb822NoDuplicateFieldsParagraphElement
Deb822NoDuplicateFieldsParagraphElement._full_size_cache
Deb822NoDuplicateFieldsParagraphElement._parent_element
Deb822NoDuplicateFieldsParagraphElement.contains_kvpair_element()
Deb822NoDuplicateFieldsParagraphElement.get_kvpair_element()
Deb822NoDuplicateFieldsParagraphElement.iter_keys()
Deb822NoDuplicateFieldsParagraphElement.iter_parts()
Deb822NoDuplicateFieldsParagraphElement.kvpair_count
Deb822NoDuplicateFieldsParagraphElement.order_after()
Deb822NoDuplicateFieldsParagraphElement.order_before()
Deb822NoDuplicateFieldsParagraphElement.order_first()
Deb822NoDuplicateFieldsParagraphElement.order_last()
Deb822NoDuplicateFieldsParagraphElement.remove_kvpair_element()
Deb822NoDuplicateFieldsParagraphElement.set_kvpair_element()
Deb822NoDuplicateFieldsParagraphElement.sort_fields()
Deb822ParagraphElement
Deb822ParagraphElement._full_size_cache
Deb822ParagraphElement._paragraph
Deb822ParagraphElement._parent_element
Deb822ParagraphElement.as_interpreted_dict_view()
Deb822ParagraphElement.configured_view()
Deb822ParagraphElement.contains_kvpair_element()
Deb822ParagraphElement.dump()
Deb822ParagraphElement.from_dict()
Deb822ParagraphElement.from_kvpairs()
Deb822ParagraphElement.get_kvpair_element()
Deb822ParagraphElement.has_duplicate_fields
Deb822ParagraphElement.iter_keys()
Deb822ParagraphElement.kvpair_count
Deb822ParagraphElement.new_empty_paragraph()
Deb822ParagraphElement.order_after()
Deb822ParagraphElement.order_before()
Deb822ParagraphElement.order_first()
Deb822ParagraphElement.order_last()
Deb822ParagraphElement.remove_kvpair_element()
Deb822ParagraphElement.set_field_from_raw_string()
Deb822ParagraphElement.set_field_to_simple_value()
Deb822ParagraphElement.set_kvpair_element()
Deb822ParagraphElement.sort_fields()
Deb822ParagraphToStrWrapperMixin
Deb822ParagraphToStrWrapperMixin._auto_map_final_newline_in_multiline_values
Deb822ParagraphToStrWrapperMixin._auto_map_initial_line_whitespace
Deb822ParagraphToStrWrapperMixin._convert_value_to_str()
Deb822ParagraphToStrWrapperMixin._discard_comments_on_read
Deb822ParagraphToStrWrapperMixin._interpret_value()
Deb822ParagraphToStrWrapperMixin._preserve_field_comments_on_field_updates
Deb822ParsedTokenList
Deb822ParsedTokenList._append_continuation_line_token_if_necessary()
Deb822ParsedTokenList._continuation_line_char
Deb822ParsedTokenList._enable_reformatting()
Deb822ParsedTokenList._generate_field_content()
Deb822ParsedTokenList._generate_kvpair()
Deb822ParsedTokenList._generate_reformatted_field_content()
Deb822ParsedTokenList._iter_content_as_tokens()
Deb822ParsedTokenList._mark_changed()
Deb822ParsedTokenList._previous_is_newline()
Deb822ParsedTokenList._remove_node()
Deb822ParsedTokenList._update_field()
Deb822ParsedTokenList.append()
Deb822ParsedTokenList.append_comment()
Deb822ParsedTokenList.append_newline()
Deb822ParsedTokenList.append_separator()
Deb822ParsedTokenList.append_value()
Deb822ParsedTokenList.clear()
Deb822ParsedTokenList.convert_to_text()
Deb822ParsedTokenList.iter_parts()
Deb822ParsedTokenList.iter_value_references()
Deb822ParsedTokenList.no_reformatting_when_finished()
Deb822ParsedTokenList.reformat_when_finished()
Deb822ParsedTokenList.remove()
Deb822ParsedTokenList.replace()
Deb822ParsedTokenList.sort()
Deb822ParsedTokenList.sort_elements()
Deb822ParsedTokenList.value_formatter()
Deb822ParsedTokenList.value_parts
Deb822ParsedValueElement
Deb822ValueElement
Deb822ValueLineElement
Deb822ValueLineElement._comment_element
Deb822ValueLineElement._continuation_line_token
Deb822ValueLineElement._iter_content_parts()
Deb822ValueLineElement._iter_content_tokens()
Deb822ValueLineElement._leading_whitespace_token
Deb822ValueLineElement._newline_token
Deb822ValueLineElement._trailing_whitespace_token
Deb822ValueLineElement._value_tokens
Deb822ValueLineElement.add_newline_if_missing()
Deb822ValueLineElement.comment_element
Deb822ValueLineElement.continuation_line_token
Deb822ValueLineElement.convert_content_to_text()
Deb822ValueLineElement.iter_parts()
Deb822ValueLineElement.newline_token
GenericContentBasedInterpretation
Interpretation
ListInterpretation
ValueReference
_abort_on_error_tokens()
_build_field_with_value()
_build_value_line()
_convert_value_lines_to_lines()
_format_comment()
_is_comma_token()
_non_end_of_line_token()
_parse_comma_list_value()
_parse_uploaders_list_value()
_parse_whitespace_list_value()
_parsed_value_render_factory()
_parser_to_value_factory()
_unpack_key()
parse_deb822_file()
- debian._deb822_repro.tokens module
Deb822CommaToken
Deb822CommentToken
Deb822ErrorToken
Deb822FieldNameToken
Deb822FieldSeparatorToken
Deb822NewlineAfterValueToken
Deb822PipeToken
Deb822SemanticallySignificantWhiteSpace
Deb822SeparatorToken
Deb822SpaceSeparatorToken
Deb822Token
Deb822Token._parent_element
Deb822Token._text
Deb822Token._token_size
Deb822Token._verify_token_text()
Deb822Token.clear_parent_if_parent()
Deb822Token.convert_to_text()
Deb822Token.is_comment
Deb822Token.is_error
Deb822Token.is_separator
Deb822Token.is_whitespace
Deb822Token.parent_element
Deb822Token.size()
Deb822Token.text
Deb822ValueContinuationToken
Deb822ValueDependencyToken
Deb822ValueDependencyVersionRelationOperatorToken
Deb822ValueToken
Deb822WhitespaceToken
_value_line_tokenizer()
comma_split_tokenizer()
tokenize_deb822_file()
whitespace_split_tokenizer()
- debian._deb822_repro.types module
Module contents¶
Round-trip safe dictionary-like interfaces to RFC822-like files
This module is a round-trip safe API for working with RFC822-like Debian data formats. It is primarily aimed files managed by humans, like debian/control. While it is be able to process any Deb822 file, you might find the debian.deb822 module better suited for larger files such as the Packages and Sources from the Debian archive due to reasons explained below.
Being round-trip safe means that this module will faithfully preserve the original formatting including whitespace and comments from the input where not modified. A concrete example:
>>> from debian._deb822_repro import parse_deb822_file
>>> example_deb822_paragraph = '''
... Package: foo
... # Field comment (because it becomes just before a field)
... Section: main/devel
... Depends: libfoo,
... # Inline comment (associated with the next line)
... libbar,
... '''
>>> deb822_file = parse_deb822_file(example_deb822_paragraph.splitlines())
>>> paragraph = next(iter(deb822_file))
>>> paragraph['Section'] = 'devel'
>>> output = deb822_file.dump()
>>> output == example_deb822_paragraph.replace('Section: main/devel', 'Section: devel')
True
This makes it particularly good for automated changes/corrections to files (partly) maintained by humans.
Compared to debian.deb822¶
The round-trip safe API is primarily useful when your program is editing files and the file in question is (likely) to be hand-edited or formatted directly by human maintainers. This includes files like debian/control and the debian/copyright using the “DEP-5” format.
The round-trip safe API also supports parsing and working with invalid files. This enables programs to work on the file in cases where the file was a left with an error in an attempt to correct it (or ignore it).
On the flip side, the debian.deb822 module generally uses less memory than the round trip safe API. In some cases, it will also have faster data structures because its internal data structures are simpler. Accordingly, when you are doing read-only work or/and working with large files a la the Packages or Sources files from the Debian archive, then the round-trip safe API either provides no advantages or its trade-offs might show up in performance statistics.
The memory and runtime performance difference should generally be constant for valid files but not necessarily a small one. For invalid files, some operations can degrade in runtime performance in particular cases (memory performance for invalid files are comparable to that of valid files).
Converting from debian.deb822¶
The following is a short example for how to migrate from debian.deb822 to the round-trip safe API. Given the following source text:
>>> dctrl_input = b'''
... Source: foo
... Build-Depends: debhelper-compat (= 13)
...
... Package: bar
... Architecture: any
... Depends: ${misc:Depends},
... ${shlibs:Depends},
... Description: provides some exciting feature
... yada yada yada
... .
... more deskription with a misspelling
... '''.lstrip() # To remove the leading newline
>>> # A few definitions to emulate file I/O (would be different in the program)
>>> import contextlib, os
>>> @contextlib.contextmanager
... def open_input():
... # Works with and without keepends=True.
... # Keep the ends here to truly emulate an open file.
... yield dctrl_input.splitlines(keepends=True)
>>> def open_output():
... return open(os.devnull, 'wb')
With debian.deb822, your code might look like this:
>>> from debian.deb822 import Deb822
>>> with open_input() as in_fd, open_output() as out_fd:
... for paragraph in Deb822.iter_paragraphs(in_fd):
... if 'Description' not in paragraph:
... continue
... description = paragraph['Description']
... # Fix typo
... paragraph['Description'] = description.replace('deskription', 'description')
... paragraph.dump(out_fd)
With the round-trip safe API, the rewrite would look like this:
>>> from debian._deb822_repro import parse_deb822_file
>>> with open_input() as in_fd, open_output() as out_fd:
... parsed_file = parse_deb822_file(in_fd)
... for paragraph in parsed_file:
... if 'Description' not in paragraph:
... continue
... description = paragraph['Description']
... # Fix typo
... paragraph['Description'] = description.replace('deskription', 'description')
... parsed_file.dump(out_fd)
Key changes are:
Imports are different.
Deb822.iter_paragraphs is replaced by parse_deb822_file and a reference to its return value is kept for later.
Instead of dumping paragraphs one by one, the return value from parse_deb822_file is dumped at the end.
The round-trip safe api does support “per-paragraph” but formatting and comments between paragraphs would be lost in the output. This may be an acceptable tradeoff or desired for some cases.
Note that the round trip safe API does not accept all the same parameters as the debian.deb822 module does. Often this is because the feature is not relevant for the round-trip safe API (e.g., python-apt cannot be used as it discard comments) or is obsolete in the debian.deb822 module and therefore omitted.
For list based fields, you may want to have a look at the Deb822ParagraphElement.as_interpreted_dict_view method.
Stability of this API¶
The API is subject to change based on feedback from early adopters and beta testers. That said, the code for valid files is unlikely to change in a backwards incompatible way.
- Things that might change in an incompatible way include:
Whether invalid files are accepted (parsed without errors) by default. (currently they are)
How invalid files are parsed. As an example, currently a syntax error acts as a paragraph separator. Whether it should is open to debate.
- exception debian._deb822_repro.AmbiguousDeb822FieldKeyError¶
Bases:
KeyError
Specialized version of KeyError to denote a valid but ambiguous field name
- This exception occurs if:
the field is accessed via a str on a configured view that does not automatically resolve ambiguous field names (see Deb822ParagraphElement.configured_view), AND
a concrete paragraph contents a repeated field (which is not valid in deb822 but the module supports parsing them)
Note that the default is to automatically resolve ambiguous fields. Accordingly you will only see this exception if you have “opted in” on wanting to know that the lookup was ambiguous.
The ambiguity can be resolved by using a tuple of (<field-name>, <filed-index>) instead of <field-name>.
- class debian._deb822_repro.Deb822FileElement(token_and_elements: LinkedList[Deb822Element | Deb822Token])¶
Bases:
Deb822Element
Represents the entire deb822 file
- _parent_element: ReferenceType[Deb822Element] | None¶
- _set_parent(t: TE) TE ¶
- append(paragraph: Deb822ParagraphElement) None ¶
Appends a paragraph to the file
>>> deb822_file = Deb822FileElement.new_empty_file() >>> para1 = Deb822ParagraphElement.new_empty_paragraph() >>> para1["Source"] = "foo" >>> para1["Build-Depends"] = "debhelper-compat (= 13)" >>> para2 = Deb822ParagraphElement.new_empty_paragraph() >>> para2["Package"] = "foo" >>> para2["Depends"] = "${shlib:Depends}, ${misc:Depends}" >>> deb822_file.append(para1) >>> deb822_file.append(para2) >>> expected = ''' ... Source: foo ... Build-Depends: debhelper-compat (= 13) ... ... Package: foo ... Depends: ${shlib:Depends}, ${misc:Depends} ... '''.lstrip() >>> deb822_file.dump() == expected True
- dump(fd)¶
- dump() str
- find_first_error_element() Deb822ErrorElement | None ¶
Returns the first Deb822ErrorElement (or None) in the file
- insert(idx: int, para: Deb822ParagraphElement) None ¶
Inserts a paragraph into the file at the given “index” of paragraphs
Note that if the index is between two paragraphs containing a “free floating” comment (e.g. paragraph/start-of-file, empty line, comment, empty line, paragraph) then it is unspecified which “side” of the comment the new paragraph will appear and this may change between versions of python-debian.
>>> original = ''' ... Package: libfoo-dev ... Depends: libfoo1 (= ${binary:Version}), ${shlib:Depends}, ${misc:Depends} ... '''.lstrip() >>> deb822_file = parse_deb822_file(original.splitlines()) >>> para1 = Deb822ParagraphElement.new_empty_paragraph() >>> para1["Source"] = "foo" >>> para1["Build-Depends"] = "debhelper-compat (= 13)" >>> para2 = Deb822ParagraphElement.new_empty_paragraph() >>> para2["Package"] = "libfoo1" >>> para2["Depends"] = "${shlib:Depends}, ${misc:Depends}" >>> deb822_file.insert(0, para1) >>> deb822_file.insert(1, para2) >>> expected = ''' ... Source: foo ... Build-Depends: debhelper-compat (= 13) ... ... Package: libfoo1 ... Depends: ${shlib:Depends}, ${misc:Depends} ... ... Package: libfoo-dev ... Depends: libfoo1 (= ${binary:Version}), ${shlib:Depends}, ${misc:Depends} ... '''.lstrip() >>> deb822_file.dump() == expected True
- property is_valid_file: bool¶
Returns true if the file is valid
Invalid elements include error elements (Deb822ErrorElement) but also issues such as paragraphs with duplicate fields or “empty” files (a valid deb822 file contains at least one paragraph).
- iter_parts() Iterable[Deb822Element | Deb822Token] ¶
- classmethod new_empty_file() Deb822FileElement ¶
Creates a new Deb822FileElement with no contents
Note that a deb822 file must be non-empty to be considered valid
- position_in_file() Position ¶
The start position of this token/element in this file
This is an expensive operation and in many cases have to traverse the entire file structure to answer the query. Consider whether you can maintain the parent’s position and then use position_in_parent() combined with child_position.relative_to(parent_position)
- position_in_parent() Position ¶
The start position of this token/element inside its parent
This is operation is generally linear to the number of “parts” (elements/tokens) inside the parent.
- remove(paragraph: Deb822ParagraphElement) None ¶
- class debian._deb822_repro.Deb822NoDuplicateFieldsParagraphElement(kvpair_elements: List[Deb822KeyValuePairElement], kvpair_order: OrderedSet)¶
Bases:
Deb822ParagraphElement
Paragraph implementation optimized for valid deb822 files
When there are no duplicated fields, we can use simpler and faster datastructures for common operations.
- _parent_element: ReferenceType[Deb822Element] | None¶
- contains_kvpair_element(item: object) bool ¶
- get_kvpair_element(item: Deb822FieldNameToken | str | Tuple[str, int], use_get: bool = False) Deb822KeyValuePairElement | None ¶
- iter_keys() Iterable[str] ¶
- iter_parts() Iterable[Deb822Element | Deb822Token] ¶
- property kvpair_count: int¶
- order_after(field: Deb822FieldNameToken | str | Tuple[str, int], reference_field: Deb822FieldNameToken | str | Tuple[str, int]) None ¶
Re-order the given field so appears directly before the reference field in the paragraph
The reference field must be present.
- order_before(field: Deb822FieldNameToken | str | Tuple[str, int], reference_field: Deb822FieldNameToken | str | Tuple[str, int]) None ¶
Re-order the given field so appears directly after the reference field in the paragraph
The reference field must be present.
- order_first(field: Deb822FieldNameToken | str | Tuple[str, int]) None ¶
Re-order the given field so it is “first” in the paragraph
- order_last(field: Deb822FieldNameToken | str | Tuple[str, int]) None ¶
Re-order the given field so it is “last” in the paragraph
- remove_kvpair_element(key: Deb822FieldNameToken | str | Tuple[str, int]) None ¶
- set_kvpair_element(key: Deb822FieldNameToken | str | Tuple[str, int], value: Deb822KeyValuePairElement) None ¶
- sort_fields(key: Callable[[str], Any] | None = None) None ¶
Re-order all fields
- Parameters:
key – Provide a key function (same semantics as for sorted). Keep in mind that the module preserve the cases for field names - in generally, callers are recommended to use “lower()” to normalize the case.
- class debian._deb822_repro.Deb822ParagraphElement¶
Bases:
Deb822Element
,Deb822ParagraphToStrWrapperMixin
,ABC
- property _paragraph: Deb822ParagraphElement¶
- _parent_element: ReferenceType[Deb822Element] | None¶
- as_interpreted_dict_view(interpretation: Interpretation[T], *, auto_resolve_ambiguous_fields: bool = True) Deb822InterpretingParagraphWrapper[T] ¶
Provide a Dict-like view of the paragraph
This method returns a dict-like object representing this paragraph and is useful for accessing fields in a given interpretation. It is possible to use multiple versions of this dict-like view with different interpretations on the same paragraph at the same time (for different fields).
>>> example_deb822_paragraph = ''' ... Package: foo ... # Field comment (because it becomes just before a field) ... Architecture: amd64 ... # Inline comment (associated with the next line) ... i386 ... # We also support arm ... arm64 ... armel ... ''' >>> dfile = parse_deb822_file(example_deb822_paragraph.splitlines()) >>> paragraph = next(iter(dfile)) >>> list_view = paragraph.as_interpreted_dict_view(LIST_SPACE_SEPARATED_INTERPRETATION) >>> # With the defaults, you only deal with the semantic values >>> # - no leading or trailing whitespace on the first part of the value >>> list(list_view["Package"]) ['foo'] >>> with list_view["Architecture"] as arch_list: ... orig_arch_list = list(arch_list) ... arch_list.replace('i386', 'kfreebsd-amd64') >>> orig_arch_list ['amd64', 'i386', 'arm64', 'armel'] >>> list(list_view["Architecture"]) ['amd64', 'kfreebsd-amd64', 'arm64', 'armel'] >>> print(paragraph.dump(), end='') Package: foo # Field comment (because it becomes just before a field) Architecture: amd64 # Inline comment (associated with the next line) kfreebsd-amd64 # We also support arm arm64 armel >>> # Format preserved and architecture replaced >>> with list_view["Architecture"] as arch_list: ... # Prettify the result as sorting will cause awkward whitespace ... arch_list.reformat_when_finished() ... arch_list.sort() >>> print(paragraph.dump(), end='') Package: foo # Field comment (because it becomes just before a field) Architecture: amd64 # We also support arm arm64 armel # Inline comment (associated with the next line) kfreebsd-amd64 >>> list(list_view["Architecture"]) ['amd64', 'arm64', 'armel', 'kfreebsd-amd64'] >>> # Format preserved and architecture values sorted
- Parameters:
interpretation – Decides how the field values are interpreted. As an example, use LIST_SPACE_SEPARATED_INTERPRETATION for fields such as Architecture in the debian/control file.
auto_resolve_ambiguous_fields – This parameter is only relevant for paragraphs that contain the same field multiple times (these are generally invalid). If the caller requests an ambiguous field from an invalid paragraph via a plain field name, the return dict-like object will refuse to resolve the field (not knowing which version to pick). This parameter (if set to True) instead changes the error into assuming the caller wants the first variant.
- configured_view(*, discard_comments_on_read: bool = True, auto_map_initial_line_whitespace: bool = True, auto_resolve_ambiguous_fields: bool = True, preserve_field_comments_on_field_updates: bool = True, auto_map_final_newline_in_multiline_values: bool = True) Deb822DictishParagraphWrapper ¶
Provide a Dict[str, str]-like view of this paragraph with non-standard parameters
This method returns a dict-like object representing this paragraph that is optionally configured differently from the default view.
>>> example_deb822_paragraph = ''' ... Package: foo ... # Field comment (because it becomes just before a field) ... Depends: libfoo, ... # Inline comment (associated with the next line) ... libbar, ... ''' >>> dfile = parse_deb822_file(example_deb822_paragraph.splitlines()) >>> paragraph = next(iter(dfile)) >>> # With the defaults, you only deal with the semantic values >>> # - no leading or trailing whitespace on the first part of the value >>> paragraph["Package"] 'foo' >>> # - no inline comments in multiline values (but whitespace will be present >>> # subsequent lines.) >>> print(paragraph["Depends"]) libfoo, libbar, >>> paragraph['Foo'] = 'bar' >>> paragraph.get('Foo') 'bar' >>> paragraph.get('Unknown-Field') is None True >>> # But you get asymmetric behaviour with set vs. get >>> paragraph['Foo'] = ' bar\n' >>> paragraph['Foo'] 'bar' >>> paragraph['Bar'] = ' bar\n#Comment\n another value\n' >>> # Note that the whitespace on the first line has been normalized. >>> print("Bar: " + paragraph['Bar']) Bar: bar another value >>> # The comment is present (in case you where wondering) >>> print(paragraph.get_kvpair_element('Bar').convert_to_text(), end='') Bar: bar #Comment another value >>> # On the other hand, you can choose to see the values as they are >>> # - We will just reset the paragraph as a "nothing up my sleeve" >>> dfile = parse_deb822_file(example_deb822_paragraph.splitlines()) >>> paragraph = next(iter(dfile)) >>> nonstd_dictview = paragraph.configured_view( ... discard_comments_on_read=False, ... auto_map_initial_line_whitespace=False, ... # For paragraphs with duplicate fields, you can choose to get an error ... # rather than the dict picking the first value available. ... auto_resolve_ambiguous_fields=False, ... auto_map_final_newline_in_multiline_values=False, ... ) >>> # Because we have reset the state, Foo and Bar are no longer there. >>> 'Bar' not in paragraph and 'Foo' not in paragraph True >>> # We can now see the comments (discard_comments_on_read=False) >>> # (The leading whitespace in front of "libfoo" is due to >>> # auto_map_initial_line_whitespace=False) >>> print(nonstd_dictview["Depends"], end='') libfoo, # Inline comment (associated with the next line) libbar, >>> # And all the optional whitespace on the first value line >>> # (auto_map_initial_line_whitespace=False) >>> nonstd_dictview["Package"] == ' foo\n' True >>> # ... which will give you symmetric behaviour with set vs. get >>> nonstd_dictview['Foo'] = ' bar \n' >>> nonstd_dictview['Foo'] ' bar \n' >>> nonstd_dictview['Bar'] = ' bar \n#Comment\n another value\n' >>> nonstd_dictview['Bar'] ' bar \n#Comment\n another value\n' >>> # But then you get no help either. >>> try: ... nonstd_dictview["Baz"] = "foo" ... except ValueError: ... print("Rejected") Rejected >>> # With auto_map_initial_line_whitespace=False, you have to include minimum a newline >>> nonstd_dictview["Baz"] = "foo\n" >>> # The absence of leading whitespace gives you the terse variant at the expensive >>> # readability >>> paragraph.get_kvpair_element('Baz').convert_to_text() 'Baz:foo\n' >>> # But because they are views, changes performed via one view is visible in the other >>> paragraph['Foo'] 'bar' >>> # The views show the values according to their own rules. Therefore, there is an >>> # asymmetric between paragraph['Foo'] and nonstd_dictview['Foo'] >>> # Nevertheless, you can read or write the fields via either - enabling you to use >>> # the view that best suit your use-case for the given field. >>> 'Baz' in paragraph and nonstd_dictview.get('Baz') is not None True >>> # Deletion via the view also works >>> del nonstd_dictview['Baz'] >>> 'Baz' not in paragraph and nonstd_dictview.get('Baz') is None True
- Parameters:
discard_comments_on_read – When getting a field value from the dict, this parameter decides how in-line comments are handled. When setting the value, inline comments are still allowed and will be retained. However, keep in mind that this option makes getter and setter asymmetric as a “get” following a “set” with inline comments will omit the comments even if they are there (see the code example).
auto_map_initial_line_whitespace – Special-case the first value line by trimming unnecessary whitespace leaving only the value. For single-line values, all space including newline is pruned. For multi-line values, the newline is preserved / needed to distinguish the first line from the following lines. When setting a value, this option normalizes the whitespace of the initial line of the value field. When this option is set to True makes the dictionary behave more like the original Deb822 module.
preserve_field_comments_on_field_updates – Whether to preserve the field comments when mutating the field.
auto_resolve_ambiguous_fields – This parameter is only relevant for paragraphs that contain the same field multiple times (these are generally invalid). If the caller requests an ambiguous field from an invalid paragraph via a plain field name, the return dict-like object will refuse to resolve the field (not knowing which version to pick). This parameter (if set to True) instead changes the error into assuming the caller wants the first variant.
auto_map_final_newline_in_multiline_values – This parameter controls whether a multiline field with have / need a trailing newline. If True, the trailing newline is hidden on get and automatically added in set (if missing). When this option is set to True makes the dictionary behave more like the original Deb822 module.
- contains_kvpair_element(item: object) bool ¶
- dump(fd)¶
- dump() str
- classmethod from_dict(mapping: Mapping[str, str]) Deb822ParagraphElement ¶
- classmethod from_kvpairs(kvpair_elements: List[Deb822KeyValuePairElement]) Deb822ParagraphElement ¶
- get_kvpair_element(item: Deb822FieldNameToken | str | Tuple[str, int], use_get: bool = False) Deb822KeyValuePairElement | None ¶
- property has_duplicate_fields: bool¶
Tell whether this paragraph has duplicate fields
- iter_keys() Iterable[Deb822FieldNameToken | str | Tuple[str, int]] ¶
- property kvpair_count: int¶
- classmethod new_empty_paragraph() Deb822ParagraphElement ¶
- order_after(field: Deb822FieldNameToken | str | Tuple[str, int], reference_field: Deb822FieldNameToken | str | Tuple[str, int]) None ¶
Re-order the given field so appears directly before the reference field in the paragraph
The reference field must be present.
- order_before(field: Deb822FieldNameToken | str | Tuple[str, int], reference_field: Deb822FieldNameToken | str | Tuple[str, int]) None ¶
Re-order the given field so appears directly after the reference field in the paragraph
The reference field must be present.
- order_first(field: Deb822FieldNameToken | str | Tuple[str, int]) None ¶
Re-order the given field so it is “first” in the paragraph
- order_last(field: Deb822FieldNameToken | str | Tuple[str, int]) None ¶
Re-order the given field so it is “last” in the paragraph
- remove_kvpair_element(key: Deb822FieldNameToken | str | Tuple[str, int]) None ¶
- set_field_from_raw_string(item: Deb822FieldNameToken | str | Tuple[str, int], raw_string_value: str, *, preserve_original_field_comment: bool | None = None, field_comment: List[str] | Deb822CommentElement | None = None) None ¶
Sets a field in this paragraph to a given text value
In many cases, it is better for callers to just use the paragraph as if it was a dictionary. However, this method does enable to you choose the field comment (if any) and lets to have a higher degree of control over whitespace (on the first line), which can be a reason for using it.
Example usage:
>>> example_deb822_paragraph = ''' ... Package: foo ... ''' >>> dfile = parse_deb822_file(example_deb822_paragraph.splitlines()) >>> p = next(iter(dfile)) >>> raw_value = ''' ... Build-Depends: debhelper-compat (= 12), ... some-other-bd, ... # Comment ... another-bd, ... '''.lstrip() # Remove leading newline, but *not* the trailing newline >>> fname, new_value = raw_value.split(':', 1) >>> p.set_field_from_raw_string(fname, new_value) >>> print(p.dump(), end='') Package: foo Build-Depends: debhelper-compat (= 12), some-other-bd, # Comment another-bd, >>> # Format preserved
- Parameters:
item – Name of the field to set. If the paragraph already contains the field, then it will be replaced. Otherwise, it is added to the end of the paragraph. Note this can be a “paragraph key”, which enables you to control which instance of a field is being replaced (in case of duplicate fields).
raw_string_value –
The text to use as the value. The text must be valid deb822 syntax and is used exactly as it is given. Accordingly, multi-line values must include mandatory leading space on continuation lines, newlines after the value, etc. On the flip-side, any optional space or comments will be included.
Note that the first line will never be read as a comment (if the first line of the value starts with a “#” then it will result in “Field-Name:#…” which is parsed as a value starting with “#” rather than a comment).
preserve_original_field_comment – If True, then if there is an existing field and that has a comment, then the comment will remain after this operation. This is the default is the field_comment parameter is omitted. Note that if the parameter is True and the item is ambiguous, this will raise an AmbiguousDeb822FieldKeyError. When the parameter is omitted, the ambiguity is resolved automatically and if the resolved field has a comment then that will be preserved (assuming field_comment is None).
field_comment –
If not None, add or replace the comment for the field. Each string in the list will become one comment line (inserted directly before the field name). Will appear in the same order as they do in the list.
If you want complete control over the formatting of the comments, then ensure that each line start with “#” and end with “n” before the call. Otherwise, leading/trailing whitespace is normalized and the missing “#”/”n” character is inserted.
- set_field_to_simple_value(item: Deb822FieldNameToken | str | Tuple[str, int], simple_value: str, *, preserve_original_field_comment: bool | None = None, field_comment: List[str] | Deb822CommentElement | None = None) None ¶
Sets a field in this paragraph to a simple “word” or “phrase”
In many cases, it is better for callers to just use the paragraph as if it was a dictionary. However, this method does enable to you choose the field comment (if any), which can be a reason for using it.
This is suitable for “simple” fields like “Package”. Example:
>>> example_deb822_paragraph = ''' ... Package: foo ... ''' >>> dfile = parse_deb822_file(example_deb822_paragraph.splitlines()) >>> p = next(iter(dfile)) >>> p.set_field_to_simple_value("Package", "mscgen") >>> p.set_field_to_simple_value("Architecture", "linux-any kfreebsd-any", ... field_comment=['Only ported to linux and kfreebsd']) >>> p.set_field_to_simple_value("Priority", "optional") >>> print(p.dump(), end='') Package: mscgen # Only ported to linux and kfreebsd Architecture: linux-any kfreebsd-any Priority: optional >>> # Values are formatted nicely by default, but it does not work with >>> # multi-line values >>> p.set_field_to_simple_value("Foo", "bar\nbin\n") Traceback (most recent call last): ... ValueError: Cannot use set_field_to_simple_value for values with newlines
- Parameters:
item – Name of the field to set. If the paragraph already contains the field, then it will be replaced. If the field exists, then it will preserve its order in the paragraph. Otherwise, it is added to the end of the paragraph. Note this can be a “paragraph key”, which enables you to control which instance of a field is being replaced (in case of duplicate fields).
simple_value – The text to use as the value. The value must not contain newlines. Leading and trailing will be stripped but space within the value is preserved. The value cannot contain comments (i.e. if the “#” token appears in the value, then it is considered a value rather than “start of a comment)
preserve_original_field_comment – See the description for the parameter with the same name in the set_field_from_raw_string method.
field_comment – See the description for the parameter with the same name in the set_field_from_raw_string method.
- set_kvpair_element(key: Deb822FieldNameToken | str | Tuple[str, int], value: Deb822KeyValuePairElement) None ¶
- sort_fields(key: Callable[[str], Any] | None = None) None ¶
Re-order all fields
- Parameters:
key – Provide a key function (same semantics as for sorted). Keep in mind that the module preserve the cases for field names - in generally, callers are recommended to use “lower()” to normalize the case.
- class debian._deb822_repro.Interpretation¶
Bases:
Generic
[T
]- interpret(kvpair_element: Deb822KeyValuePairElement, discard_comments_on_read: bool = True) T ¶
- exception debian._deb822_repro.SyntaxOrParseError¶
Bases:
ValueError
Specialized version of ValueError for syntax/parse errors.
- debian._deb822_repro.parse_deb822_file(sequence: Iterable[str | bytes] | str, *, accept_files_with_error_tokens: bool = False, accept_files_with_duplicated_fields: bool = False, encoding: str = 'utf-8') Deb822FileElement ¶
- Parameters:
sequence – An iterable over lines of str or bytes (an open file for reading will do). If line endings are provided in the input, then they must be present on every line (except the last) will be preserved as-is. If omitted and the content is at least 2 lines, then parser will assume implicit newlines.
accept_files_with_error_tokens – If True, files with critical syntax or parse errors will be returned as “successfully” parsed. Usually, working on files with this kind of errors are not desirable as it is hard to make sense of such files (and they might in fact not be a deb822 file at all). When set to False (the default) a ValueError is raised if there is a critical syntax or parse error. Note that duplicated fields in a paragraph is not considered a critical parse error by this parser as the implementation can gracefully cope with these. Use accept_files_with_duplicated_fields to determine if such files should be accepted.
accept_files_with_duplicated_fields – If True, then files containing paragraphs with duplicated fields will be returned as “successfully” parsed even though they are invalid according to the specification. The paragraphs will prefer the first appearance of the field unless caller explicitly requests otherwise (e.g., via Deb822ParagraphElement.configured_view). If False, then this method will raise a ValueError if any duplicated fields are seen inside any paragraph.
encoding – The encoding to use (this is here to support Deb822-like APIs, new code should not use this parameter).