pvl package

Submodules

pvl.collections module

Parameter Value Language container datatypes providing enhancements to Python general purpose built-in containers.

To enable efficient operations on parsed PVL text, we need an object that acts as both a dict-like Mapping container and a list-like Sequence container, essentially an ordered multi-dict. There is no existing object or even an Abstract Base Class in the Python Standard Library for such an object. So we define the MutableMappingSequence ABC here, which is (as the name implies) an abstract base class that implements both the Python MutableMapping and Mutable Sequence ABCs. We also provide two implementations, the OrderedMultiDict, and the newer PVLMultiDict.

Additionally, for PVL Values which also have an associated PVL Units Expression, they need to be returned as a quantity object which contains both a notion of a value and the units for that value. Again, there is no fundamental Python type for a quantity, so we define the Quantity class (formerly the Units class).

class pvl.collections.ItemsView(mapping)[source]

Bases: pvl.collections.MappingView

index(item)[source]
class pvl.collections.KeysView(mapping)[source]

Bases: pvl.collections.MappingView

index(key)[source]
class pvl.collections.MappingView(mapping)[source]

Bases: object

class pvl.collections.MutableMappingSequence[source]

Bases: collections.abc.MutableMapping, collections.abc.MutableSequence

ABC for a mutable object that has both mapping and sequence characteristics.

Must implement .getall(k) and .popall(k) since a MutableMappingSequence can have many values for a single key, while .get(k) and .pop(k) return and operate on a single value, the all versions return and operate on all values in the MutableMappingSequence with the key k.

Furthermore, .pop() without an argument should function as the MutableSequence pop() function and pop the last value when considering the MutableMappingSequence in a list-like manner.

append(key, value)[source]

S.append(value) – append value to the end of the sequence

getall(key)[source]
popall(key)[source]
class pvl.collections.OrderedMultiDict(*args, **kwargs)[source]

Bases: dict, pvl.collections.MutableMappingSequence

A dict like container.

This container preserves the original ordering as well as allows multiple values for the same key. It provides similar semantics to a list of tuples but with dict style access.

Using __setitem__ syntax overwrites all fields with the same key and __getitem__ will return the first value with the key.

append(key, value)[source]

Adds a (name, value) pair, doesn’t overwrite the value if it already exists.

clear() → None. Remove all items from D.[source]
copy() → a shallow copy of D[source]
discard(key)[source]
extend(*args, **kwargs)[source]

Add key value pairs for an iterable.

get(k[, d]) → D[k] if k in D, else d. d defaults to None.
getall(key) → collections.abc.Sequence[source]

Returns a list of all the values for a named field. Returns KeyError if the key doesn’t exist.

getlist(key) → collections.abc.Sequence[source]

Returns a list of all the values for the named field. Returns an empty list if the key doesn’t exist.

insert(index: int, *args) → None[source]

Inserts at the index given by index.

The first positional argument will always be taken as the index for insertion.

If three arguments are given, the second will be taken as the key, and the third as the value to insert.

If only two arguments are given, the second must be a sequence.

If it is a sequence of pairs (such that every item in the sequence is itself a sequence of length two), that sequence will be inserted as key, value pairs.

If it happens to be a sequence of two items (the first of which is not a sequence), the first will be taken as the key and the second the value to insert.

insert_after(key, new_item: collections.abc.Iterable, instance=0)[source]

Insert an item after a key

insert_before(key, new_item: collections.abc.Iterable, instance=0)[source]

Insert an item before a key

items() → a set-like object providing a view on D's items[source]
key_index(key, instance: int = 0) → int[source]

Get the index of the key to insert before or after.

keys() → a set-like object providing a view on D's keys[source]
pop(*args, **kwargs)[source]

Removes all items with the specified key.

popall(key, default=<object object>)

D.pop(k[,d]) -> v, remove specified key and return the corresponding value. If key is not found, d is returned if given, otherwise KeyError is raised.

popitem() → (k, v), remove and return some (key, value) pair as a[source]

2-tuple; but raise KeyError if D is empty.

update([E, ]**F) → None. Update D from mapping/iterable E and F.

If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v

values() → an object providing a view on D's values[source]
class pvl.collections.PVLAggregation(*args, **kwargs)[source]

Bases: pvl.collections.OrderedMultiDict

class pvl.collections.PVLGroup(*args, **kwargs)[source]

Bases: pvl.collections.PVLAggregation

class pvl.collections.PVLModule(*args, **kwargs)[source]

Bases: pvl.collections.OrderedMultiDict

class pvl.collections.PVLObject(*args, **kwargs)[source]

Bases: pvl.collections.PVLAggregation

class pvl.collections.Quantity[source]

Bases: pvl.collections.Quantity

A simple collections.namedtuple object to contain a value and units parameter.

If you need more comprehensive units handling, you may want to use the astropy.units.Quantity object, the pint.Quantity object, or some other 3rd party object. Please see the documentation on Quantities: Values and Units for how to use 3rd party Quantity objects with pvl.

class pvl.collections.Units[source]

Bases: pvl.collections.Quantity

class pvl.collections.ValuesView(mapping)[source]

Bases: pvl.collections.MappingView

index(value)[source]
pvl.collections.dict_delitem

Delete self[key].

pvl.collections.dict_setitem

Set self[key] to value.

pvl.decoder module

Parameter Value Language decoder.

The definition of PVL used in this module is based on the Consultive Committee for Space Data Systems, and their Parameter Value Language Specification (CCSD0006 and CCSD0008), CCSDS 6441.0-B-2, referred to as the Blue Book with a date of June 2000.

A decoder deals with converting strings given to it (typically by the parser) to the appropriate Python type.

class pvl.decoder.ODLDecoder(grammar=None, quantity_cls=None, real_cls=None)[source]

Bases: pvl.decoder.PVLDecoder

A decoder based on the rules in the PDS3 Standards Reference (version 3.8, 27 Feb 2009) Chapter 12: Object Description Language Specification and Usage.

Extends PVLDecoder, and if grammar is not specified, it will default to an ODLGrammar() object.

decode_datetime(value: str)[source]

Extends parent function to also deal with datetimes and times with a time zone offset.

If it cannot, it will raise a ValueError.

decode_non_decimal(value: str) → int[source]

Extends parent function by allowing the wider variety of radix values that ODL permits over PVL.

decode_quoted_string(value: str) → str[source]

Extends parent function because the ODL specification allows for a dash (-) line continuation character that results in the dash, the line end, and any leading whitespace on the next line to be removed. It also allows for a sequence of format effectors surrounded by spacing characters to be collapsed to a single space.

decode_unquoted_string(value: str) → str[source]

Extends parent function to provide the extra enforcement that only ODL Identifier text may be unquoted as a value.

static is_identifier(value)[source]

Returns true if value is an ODL Identifier, false otherwise.

An ODL Identifier is composed of letters, digits, and underscores. The first character must be a letter, and the last must not be an underscore.

class pvl.decoder.OmniDecoder(grammar=None, quantity_cls=None, real_cls=None)[source]

Bases: pvl.decoder.ODLDecoder

A permissive decoder that attempts to parse all forms of “PVL” that are thrown at it.

Extends ODLDecoder.

decode_datetime(value: str)[source]

Returns an appropriate Python datetime time, date, or datetime object by using the 3rd party dateutil library (if present) to parse an ISO 8601 datetime string in value. If it cannot, or the dateutil library is not present, it will raise a ValueError.

decode_non_decimal(value: str) → int[source]

Extends parent function by allowing a plus or minus sign to be in two different positions in a non-decimal number, since PVL has one specification, and ODL has another.

decode_unquoted_string(value: str) → str[source]

Overrides parent function since the ODLDecoder has a more narrow definition of what is allowable as an unquoted string than the PVLDecoder does.

class pvl.decoder.PDSLabelDecoder(grammar=None, quantity_cls=None)[source]

Bases: pvl.decoder.ODLDecoder

A decoder based on the rules in the PDS3 Standards Reference (version 3.8, 27 Feb 2009) Chapter 12: Object Description Language Specification and Usage.

Extends ODLDecoder, and if grammar is not specified, it will default to a PDS3Grammar() object.

decode_datetime(value: str)[source]

Overrides parent function since PDS3 forbids a timezone specification, and times with a precision more than miliseconds.

If it cannot decode properly, it will raise a ValueError.

class pvl.decoder.PVLDecoder(grammar=None, quantity_cls=None, real_cls=None)[source]

Bases: object

A decoder based on the rules in the CCSDS-641.0-B-2 ‘Blue Book’ which defines the PVL language.

Parameters:
  • grammar – defaults to a pvl.grammar.PVLGrammar, but can be any object that implements the pvl.grammar interface.
  • quantity_cls – defaults to pvl.collections.Quantity, but could be any class object that takes two arguments, where the first is the value, and the second is the units value.
  • real_cls – defaults to float, but could be any class object that can be constructed from a str object.
decode(value: str)[source]

Returns a Python object based on value.

decode_datetime(value: str)[source]

Takes a string and attempts to convert it to the appropriate Python datetime time, date, or datetime type based on this decoder’s grammar, or in one case, a str.

The PVL standard allows for the seconds value to range from zero to 60, so that the 60 can accommodate leap seconds. However, the Python datetime classes don’t support second values for more than 59 seconds.

If a time with 60 seconds is encountered, it will not be returned as a datetime object (since that is not representable via Python datetime objects), but simply as a string.

The user can then then try and use the time module to parse this string into a time.struct_time. We chose not to do this with pvl because time.struct_time is a full datetime like object, even if it parsed only a time like object, the year, month, and day values in the time.struct_time would default, which could be misleading.

Alternately, the pvl.grammar.PVLGrammar class contains two regexes: leap_second_Ymd_re and leap_second_Yj_re which could be used along with the re.match object’s groupdict() function to extract the string representations of the various numerical values, cast them to the appropriate numerical types, and do something useful with them.

decode_decimal(value: str)[source]

Returns a Python int or self.real_cls object, as appropriate based on value. Raises a ValueError otherwise.

decode_non_decimal(value: str) → int[source]

Returns a Python int as decoded from value on the assumption that value conforms to a non-decimal integer value as defined by this decoder’s grammar, raises ValueError otherwise.

decode_quantity(value, unit)[source]

Returns a Python object that represents a value with an associated unit, based on the values provided via value and unit. This function creates an object based on the decoder’s quantity_cls.

decode_quoted_string(value: str) → str[source]

Returns a Python str if value begins and ends with matching quote characters based on this decoder’s grammar. Raises ValueError otherwise.

decode_simple_value(value: str)[source]

Returns a Python object based on value, assuming that value can be decoded as a PVL Simple Value:

<Simple-Value> ::= (<Date-Time> | <Numeric> | <String>)
decode_unquoted_string(value: str) → str[source]

Returns a Python str if value can be decoded as an unquoted string, based on this decoder’s grammar. Raises a ValueError otherwise.

is_leap_seconds(value: str) → bool[source]

Returns True if value is a time that matches the grammar’s definition of a leap seconds time (a time string with a value of 60 for the seconds value). False otherwise.

pvl.decoder.for_try_except(exception, function, *iterable)[source]

Return the result of the first successful application of function to an element of iterable. If the function raises an Exception of type exception, it will continue to the next item of iterable. If there are no successful applications an Exception of type exception will be raised.

If additional iterable arguments are passed, function must take that many arguments and is applied to the items from all iterables in parallel (like map()). With multiple iterables, the iterator stops when the shortest iterable is exhausted.

pvl.encoder module

Parameter Value Langage encoder.

An encoder deals with converting Python objects into string values that conform to a PVL specification.

class pvl.encoder.ISISEncoder(grammar=None, decoder=None, indent=2, width=80, aggregation_end=True, end_delimiter=False, newline='n', group_class=<class 'pvl.collections.PVLGroup'>, object_class=<class 'pvl.collections.PVLObject'>)[source]

Bases: pvl.encoder.PVLEncoder

An encoder for writing PVL text that can be parsed by the ISIS PVL text parser.

The ISIS3 implementation (as of 3.9) of PVL/ODL (like) does not strictly follow any of the published standards. It was based on PDS3 ODL from the 1990s, but has several extensions adopted from existing and prior data sets from ISIS2, PDS, JAXA, ISRO, …, and extensions used only within ISIS files (cub, net). This is one of the reasons using ISIS cube files or PVL text written by ISIS as an archive format has been strongly discouraged.

Since there is no specification, only a detailed analysis of the ISIS software that parses and writes its PVL text would yield a strategy for parsing it. This encoder is most likely the least reliable for that reason. We welcome bug reports to help extend our coverage of this flavor of PVL text.

Parameters:
  • grammar – defaults to pvl.grammar.ISISGrammar().
  • decoder – defaults to pvl.decoder.PVLDecoder().
  • end_delimiter – defaults to False.
  • newline – defaults to ‘\n’.
class pvl.encoder.ODLEncoder(grammar=None, decoder=None, indent=2, width=80, aggregation_end=True, end_delimiter=False, newline='rn', group_class=<class 'pvl.collections.PVLGroup'>, object_class=<class 'pvl.collections.PVLObject'>)[source]

Bases: pvl.encoder.PVLEncoder

An encoder based on the rules in the PDS3 Standards Reference (version 3.8, 27 Feb 2009) Chapter 12: Object Description Language Specification and Usage for ODL only. This is almost certainly not what you want. There are very rarely cases where you’d want to use ODL that you wouldn’t also want to use the PDS Label restrictions, so you probably really want the PDSLabelEncoder class, not this one. Move along.

It extends PVLEncoder.

Parameters:
  • grammar – defaults to pvl.grammar.ODLGrammar().
  • decoder – defaults to pvl.decoder.ODLDecoder().
  • end_delimiter – defaults to False.
  • newline – defaults to ‘\r\n’.
encode(module: collections.abc.Mapping) → str[source]

Extends parent function, but ODL requires that there must be a spacing or format character after the END statement and this adds the encoder’s newline sequence.

encode_assignment(key, value, level=0, key_len=None) → str[source]

Overrides parent function by restricting the length of keywords and enforcing that they be ODL Identifiers and uppercasing their characters.

encode_sequence(value) → str[source]

Extends parent function, as ODL only allows one- and two-dimensional sequences of ODL scalar_values.

encode_set(values) → str[source]

Extends parent function, ODL only allows sets to contain scalar values.

encode_string(value)[source]

Extends parent function by appropriately quoting Symbol Strings.

encode_time(value: datetime.time) → str[source]

Extends parent function since ODL allows a time zone offset from UTC to be included, and otherwise recommends that times be suffixed with a ‘Z’ to clearly indicate that they are in UTC.

encode_units(value) → str[source]

Overrides parent function since ODL limits what characters and operators can be present in Units Expressions.

encode_value(value)[source]

Extends parent function by only allowing Units Expressions for numeric values.

is_assignment_statement(s) → bool[source]

Returns true if s is an ODL Assignment Statement, false otherwise.

An ODL Assignment Statement is either an element_identifier or a namespace_identifier joined to an element_identifier with a colon.

is_scalar(value) → bool[source]

Returns a boolean indicating whether the value object qualifies as an ODL ‘scalar_value’.

ODL defines a ‘scalar-value’ as a numeric_value, a date_time_string, a text_string_value, or a symbol_value.

For Python, these correspond to the following:

  • numeric_value: any of self.numeric_types, and Quantity whose value is one of the self.numeric_types.
  • date_time_string: datetime objects
  • text_string_value: str
  • symbol_value: str
is_symbol(value) → bool[source]

Returns true if value is an ODL Symbol String, false otherwise.

An ODL Symbol String is enclosed by single quotes and may not contain any of the following characters:

  1. The apostrophe, which is reserved as the symbol string delimiter.
  2. ODL Format Effectors
  3. Control characters

This means that an ODL Symbol String is a subset of the PVL quoted string, and will be represented in Python as a str.

needs_quotes(s: str) → bool[source]

Return true if s is an ODL Identifier, false otherwise.

Overrides parent function.

class pvl.encoder.PDSLabelEncoder(grammar=None, decoder=None, indent=2, width=80, aggregation_end=True, group_class=<class 'pvl.collections.PVLGroup'>, object_class=<class 'pvl.collections.PVLObject'>, convert_group_to_object=True, tab_replace=4, symbol_single_quote=True, time_trailing_z=True)[source]

Bases: pvl.encoder.ODLEncoder

An encoder based on the rules in the PDS3 Standards Reference (version 3.8, 27 Feb 2009) Chapter 12: Object Description Language Specification and Usage and writes out labels that conform to the PDS 3 standards.

It extends ODLEncoder.

You are not allowed to chose end_delimiter or newline as the parent class allows, because to be PDS-compliant, those are fixed choices. However, in some cases, the PDS3 Standards are asymmetric, allowing for a wider variety of PVL-text on “read” and a more narrow variety of PVL-text on “write”. The default values of the PDSLabelEncoder enforce those strict “write” rules, but if you wish to alter them, but still produce PVL-text that would validate against the PDS3 standard, you may alter them.

Parameters:
  • convert_group_to_object – Defaults to True, meaning that if a GROUP does not conform to the PDS definition of a GROUP, then it will be written out as an OBJECT. If it is False, then an exception will be thrown if incompatible GROUPs are encountered. In PVL and ODL, the OBJECT and GROUP aggregations are interchangeable, but the PDS applies restrictions to what can appear in a GROUP.
  • tab_replace – Defaults to 4 and indicates the number of space characters to replace horizontal tab characters with (since tabs aren’t allowed in PDS labels). If this is set to zero, tabs will not be replaced with spaces.
  • symbol_single_quotes – Defaults to True, and if a Python str object qualifies as a PVL Symbol String, it will be written to PVL-text as a single-quoted string. If False, no special handling is done, and any PVL Symbol String will be treated as a PVL Text String, which is typically enclosed with double-quotes.
  • time_trailing_z – defaults to True, and suffixes a “Z” to datetimes and times written to PVL-text as the PDS encoding standard requires. If False, no trailing “Z” is written.
count_aggs(module: collections.abc.Mapping, obj_count: int = 0, grp_count: int = 0) -> (<class 'int'>, <class 'int'>)[source]

Returns the count of OBJECT and GROUP aggregations that are contained within the module as a two-tuple in that order.

encode(module: collections.abc.MutableMapping) → str[source]

Extends the parent function, by adding a restriction. For PDS, if there are any GROUP elements, there must be at least one OBJECT element in the label. Behavior here depends on the value of this encoder’s convert_group_to_object property.

encode_aggregation_block(key, value, level=0)[source]

Extends parent function because PDS has restrictions on what may be in a GROUP.

If the encoder’s convert_group_to_object parameter is True, and a GROUP does not conform to the PDS definition of a GROUP, then it will be written out as an OBJECT. If it is False, then an exception will be thrown.

encode_set(values) → str[source]

Extends parent function because PDS only allows symbol values and integers within sets.

encode_string(value)[source]

Extends parent function to treat Symbol Strings as Text Strings, which typically means that they are double-quoted and not single-quoted.

encode_time(value: datetime.time) → str[source]

Overrides parent’s encode_time() function because even though ODL allows for timezones, PDS does not.

Not in the section on times, but at the end of the PDS ODL document, in section 12.7.3, para 14, it indicates that alternate time zones may not be used in a PDS label, only these: 1. YYYY-MM-DDTHH:MM:SS.SSS. 2. YYYY-DDDTHH:MM:SS.SSS.

is_PDSgroup(group: collections.abc.Mapping) → bool[source]

Returns true if the dict-like group qualifies as a PDS Group, false otherwise.

PDS applies the following restrictions to GROUPS:

  1. The GROUP structure may only be used in a data product label which also contains one or more data OBJECT definitions.
  2. The GROUP statement must contain only attribute assignment statements, include pointers, or related information pointers (i.e., no data location pointers). If there are multiple values, a single statement must be used with either sequence or set syntax; no attribute assignment statement or pointer may be repeated.
  3. GROUP statements may not be nested.
  4. GROUP statements may not contain OBJECT definitions.
  5. Only PSDD elements may appear within a GROUP statement. PSDD is not defined anywhere in the PDS document, so don’t know how to test for it.
  6. The keyword contents associated with a specific GROUP identifier must be identical across all labels of a single data set (with the exception of the “PARAMETERS” GROUP, as explained).

Use of the GROUP structure must be coordinated with the responsible PDS discipline Node.

Items 1 & 6 and the final sentence above, can’t really be tested by examining a single group, but must be dealt with in a larger context. The ODLEncoder.encode_module() handles #1, at least. You’re on your own for the other two issues.

Item 5: PSDD is not defined anywhere in the ODL PDS document, so don’t know how to test for it.

class pvl.encoder.PVLEncoder(grammar=None, decoder=None, indent: int = 2, width: int = 80, aggregation_end: bool = True, end_delimiter: bool = True, newline: str = 'n', group_class=<class 'pvl.collections.PVLGroup'>, object_class=<class 'pvl.collections.PVLObject'>)[source]

Bases: object

An encoder based on the rules in the CCSDS-641.0-B-2 ‘Blue Book’ which defines the PVL language.

Parameters:
  • grammar – A pvl.grammar object, if None or not specified, it will be set to the grammar parameter of decoder (if decoder is not None) or will default to PVLGrammar().
  • grammar – defaults to pvl.grammar.PVLGrammar().
  • decoder – defaults to pvl.decoder.PVLDecoder().
  • indent – specifies the number of spaces that will be used to indent each level of the PVL document, when Groups or Objects are encountered, defaults to 2.
  • width – specifies the number of characters in width that each line should have, defaults to 80.
  • aggregation_end – when True the encoder will print the value of the aggregation’s Block Name in the End Aggregation Statement (e.g. END_GROUP = foo), and when false, it won’t (e.g. END_GROUP). Defaults to True.
  • end_delimiter – when True the encoder will print the grammar’s delimiter (e.g. ‘;’ for PVL) after each statement, when False it won’t. Defaults to True.
  • newline – is the string that will be placed at the end of each ‘line’ of output (and counts against width), defaults to ‘\n’.
  • group_class – must this class will be tested against with isinstance() to determine if various elements of the dict-like passed to encode() should be encoded as a PVL Group or PVL Object, defaults to PVLGroup.
  • object_class – must be a class that can take a group_class object in its constructor (essentially converting a group_class to an object_class), otherwise will raise TypeError. Defaults to PVLObject.
add_quantity_cls(cls, value_prop: str, units_prop: str)[source]

Adds a quantity class to the list of possible quantities that this encoder can handle.

Parameters:
  • cls – The name of a quantity class that can be tested with isinstance().
  • value_prop – A string that is the property name of cls that contains the value or magnitude of the quantity object.
  • units_prop – A string that is the property name of cls that contains the units element of the quantity object.
encode(module: collections.abc.Mapping) → str[source]

Returns a str formatted as a PVL document based on the dict-like module object according to the rules of this encoder.

encode_aggregation_block(key: str, value: collections.abc.Mapping, level: int = 0) → str[source]

Returns a str formatted as a PVL Aggregation Block with key as its name, and its contents based on the dict-like value object according to the rules of this encoder, with an indentation level of level.

encode_assignment(key: str, value, level: int = 0, key_len: int = None) → str[source]

Returns a str formatted as a PVL Assignment Statement with key as its Parameter Name, and its value based on value object according to the rules of this encoder, with an indentation level of level. It also allows for an optional key_len which indicates the width in characters that the Assignment Statement should be set to, defaults to the width of key.

static encode_date(value: datetime.date) → str[source]

Returns a str formatted as a PVL Date based on the value object according to the rules of this encoder.

encode_datetime(value: datetime.datetime) → str[source]

Returns a str formatted as a PVL Date/Time based on the value object according to the rules of this encoder.

encode_datetype(value) → str[source]

Returns a str formatted as a PVL Date/Time based on the value object according to the rules of this encoder. If value is not a datetime date, time, or datetime object, it will raise TypeError.

encode_module(module: collections.abc.Mapping, level: int = 0) → str[source]

Returns a str formatted as a PVL module based on the dict-like module object according to the rules of this encoder, with an indentation level of level.

encode_quantity(value) → str[source]

Returns a str formatted as a PVL Value followed by a PVL Units Expression if the value object can be encoded this way, otherwise raise ValueError.

encode_sequence(value: collections.abc.Sequence) → str[source]

Returns a str formatted as a PVL Sequence based on the value object according to the rules of this encoder.

encode_set(value: collections.abc.Set) → str[source]

Returns a str formatted as a PVL Set based on the value object according to the rules of this encoder.

encode_setseq(values: collections.abc.Collection) → str[source]

This function provides shared functionality for encode_sequence() and encode_set().

encode_simple_value(value) → str[source]

Returns a str formatted as a PVL Simple Value based on the value object according to the rules of this encoder.

encode_string(value) → str[source]

Returns a str formatted as a PVL String based on the value object according to the rules of this encoder.

static encode_time(value: datetime.time) → str[source]

Returns a str formatted as a PVL Time based on the value object according to the rules of this encoder.

encode_units(value: str) → str[source]

Returns a str formatted as a PVL Units Value based on the value object according to the rules of this encoder.

encode_value(value) → str[source]

Returns a str formatted as a PVL Value based on the value object according to the rules of this encoder.

encode_value_units(value, units) → str[source]

Returns a str formatted as a PVL Value from value followed by a PVL Units Expressions from units.

format(s: str, level: int = 0) → str[source]

Returns a string derived from s, which has leading space characters equal to level times the number of spaces specified by this encoder’s indent property.

It uses the textwrap library to wrap long lines.

needs_quotes(s: str) → bool[source]

Returns true if s must be quoted according to this encoder’s grammar, false otherwise.

class pvl.encoder.QuantTup[source]

Bases: pvl.encoder.QuantTup

This class is just a convenient namedtuple for internally keeping track of quantity classes that encoders can deal with. In general, users should not be instantiating this, instead use your encoder’s add_quantity_cls() function.

pvl.exceptions module

Exceptions for the Parameter Value Library.

exception pvl.exceptions.LexerError(msg, doc, pos, lexeme)[source]

Bases: ValueError

Subclass of ValueError with the following additional properties:

msg: The unformatted error message doc: The PVL text being parsed pos: The start index in doc where parsing failed lineno: The line corresponding to pos colno: The column corresponding to pos

exception pvl.exceptions.ParseError(msg, token=None)[source]

Bases: Exception

An exception to signal errors in the pvl parser.

exception pvl.exceptions.QuantityError[source]

Bases: Exception

A simple exception to distinguish errors from Quantity classes.

pvl.exceptions.firstpos(sub: str, pos: int)[source]

On the assumption that sub is a substring contained in a longer string, and pos is the index in that longer string of the final character in sub, returns the position of the first character of sub in that longer string.

This is useful in the PVL library when we know the position of the final character of a token, but want the position of the first character.

pvl.exceptions.linecount(doc: str, end: int, start: int = 0)[source]

Returns the number of lines (by counting the number of newline characters n, with the first line being line number one) in the string doc between the positions start and end.

pvl.grammar module

Describes the language aspects of PVL dialects.

These grammar objects are not particularly meant to be easily user-modifiable during running of an external program, which is why they have no arguments at initiation time, nor are there any methods or functions to modify them. This is because these grammar objects are used both for reading and writing PVL-text. As such, objects like PVLGrammar and ODLGrammar shouldn’t be altered, because if they are, then the PVL-text written out with them wouldn’t conform to the spec.

Certainly, these objects do have attributes that can be altered, but unless you’ve carefully read the code, it isn’t recommended.

Maybe someday we’ll add a more user-friendly interface to allow that, but in the meantime, just leave an Issue on the GitHub repo.

class pvl.grammar.ISISGrammar[source]

Bases: pvl.grammar.PVLGrammar

This defines the ISIS version of PVL.

This is valid as of ISIS 3.9, and before, at least.

The ISIS ‘Pvl’ object typically writes out parameter values and keywords in CamelCase (e.g. ‘Group’, ‘End_Group’, ‘CenterLatitude’, etc.), but it will accept all-uppercase versions.

Technically, since the ISIS ‘Pvl’ object which parses PVL text into C++ objects for ISIS programs to work with does not recognize the ‘BEGIN_<GROUP|OBJECT>’ construction, this means that ISIS does not parse PVL text that would be valid according to the PVL, ODL, or PDS3 specs.

static adjust_reserved_characters(chars: collections.abc.Iterable)[source]
comments = (('/*', '*/'), ('#', '\n'))
group_keywords = {'GROUP': 'END_GROUP'}
group_pref_keywords = ('Group', 'End_Group')
object_keywords = {'OBJECT': 'END_OBJECT'}
object_pref_keywords = ('Object', 'End_Object')
class pvl.grammar.ODLGrammar[source]

Bases: pvl.grammar.PVLGrammar

This defines an ODL grammar.

The reference for this grammar is the PDS3 Standards Reference (version 3.8, 27 Feb 2009) Chapter 12: Object Description Language Specification and Usage.

char_allowed(char)[source]

Returns true if char is allowed in the ODL Character Set.

The ODL Character Set is limited to ASCII. This is fewer characters than PVL, but appears to allow more control characters to be in quoted strings than PVL does.

default_timezone = None
group_pref_keywords = ('GROUP', 'END_GROUP')
leap_second_Yj_re = None
leap_second_Ymd_re = None
nondecimal_pre_re = re.compile('(?P<radix>[2-9]|1[0-6])#(?P<sign>[+-]?)')
nondecimal_re = re.compile('(?P<radix>[2-9]|1[0-6])#(?P<sign>[+-]?)(?P<non_decimal>[0-9A-Fa-f]+)#')
object_pref_keywords = ('OBJECT', 'END_OBJECT')
class pvl.grammar.OmniGrammar[source]

Bases: pvl.grammar.PVLGrammar

A broadly permissive grammar.

This grammar does not follow a specification, but is meant to allow the broadest possible ingestion of PVL-like text that is found.

This grammar should not be used to write out Python objects to PVL, instead please use one of the grammars that follows a published specification, like the PVLGrammar or the ODLGrammar.

char_allowed(char)[source]

Takes all characters, could accept bad things, and the user must beware.

comments = (('/*', '*/'), ('#', '\n'))
nondecimal_pre_re = re.compile('(?P<sign>[+-]?)(?P<radix>[2-9]|1[0-6])#(?P<second_sign>[+-]?)')
nondecimal_re = re.compile('(?P<sign>[+-]?)(?P<radix>[2-9]|1[0-6])#(?P<second_sign>[+-]?)(?P<non_decimal>[0-9A-Fa-f]+)#')
class pvl.grammar.PDSGrammar[source]

Bases: pvl.grammar.ODLGrammar

This defines a PDS3 ODL grammar.

The reference for this grammar is the PDS3 Standards Reference (version 3.8, 27 Feb 2009) Chapter 12: Object Description Language Specification and Usage.

default_timezone = datetime.timezone.utc
class pvl.grammar.PVLGrammar[source]

Bases: object

Describes a PVL grammar for use by the lexer and parser.

The reference for this grammar is the CCSDS-641.0-B-2 ‘Blue Book’.

aggregation_keywords = {'BEGIN_GROUP': 'END_GROUP', 'BEGIN_OBJECT': 'END_OBJECT', 'GROUP': 'END_GROUP', 'OBJECT': 'END_OBJECT'}
binary_re = re.compile('(?P<sign>[+-]?)(?P<radix>2)#(?P<non_decimal>[01]+)#')
char_allowed(char)[source]

Returns true if char is allowed in the PVL Character Set.

This is defined as most of the ISO 8859-1 ‘latin-1’ character set with some exclusions.

comments = (('/*', '*/'),)
d = '%Y-%j'
date_formats = ('%Y-%m-%d', '%Y-%j', '%Y-%m-%dZ', '%Y-%jZ')
datetime_formats = ['%Y-%m-%dT%H:%M', '%Y-%m-%dT%H:%MZ', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%S.%f', '%Y-%m-%dT%H:%M:%S.%fZ', '%Y-%jT%H:%M', '%Y-%jT%H:%MZ', '%Y-%jT%H:%M:%S', '%Y-%jT%H:%M:%SZ', '%Y-%jT%H:%M:%S.%f', '%Y-%jT%H:%M:%S.%fZ']
default_timezone = datetime.timezone.utc
delimiters = (';',)
end_statements = ('END',)
false_keyword = 'FALSE'
format_effectors = ('\n', '\r', '\x0b', '\x0c')
group_keywords = {'BEGIN_GROUP': 'END_GROUP', 'GROUP': 'END_GROUP'}
group_pref_keywords = ('BEGIN_GROUP', 'END_GROUP')
hex_re = re.compile('(?P<sign>[+-]?)(?P<radix>16)#(?P<non_decimal>[0-9A-Fa-f]+)#')
leap_second_Yj_re = re.compile('((?P<year>\\d{3}[1-9])-(?P<doy>(00[1-9]|0[1-9]\\d)|[12]\\d{2}|3[0-5]\\d|36[0-6])T)?(?P<hour>0\\d|1\\d|2[0-3]):(?P<minute>[0-5]\\d):60(\\.(?P<microsecond>\\d+))?Z?')
leap_second_Ymd_re = re.compile('((?P<year>\\d{3}[1-9])-(?P<month>0[1-9]|1[0-2])-(?P<day>0[1-9]|[12]\\d|3[01])T)?(?P<hour>0\\d|1\\d|2[0-3]):(?P<minute>[0-5]\\d):60(\\.(?P<microsecond>\\d+))?Z?')
nondecimal_pre_re = re.compile('(?P<sign>[+-]?)(?P<radix>2|8|16)#')
nondecimal_re = re.compile('(?P<sign>[+-]?)(?P<radix>2|8|16)#(?P<non_decimal>[0-9|A-Fa-f]+)#')
none_keyword = 'NULL'
numeric_start_chars = ('+', '-')
object_keywords = {'BEGIN_OBJECT': 'END_OBJECT', 'OBJECT': 'END_OBJECT'}
object_pref_keywords = ('BEGIN_OBJECT', 'END_OBJECT')
octal_re = re.compile('(?P<sign>[+-]?)(?P<radix>8)#(?P<non_decimal>[0-7]+)#')
p = ('BEGIN_OBJECT', 'END_OBJECT')
quotes = ('"', "'")
reserved_characters = ('&', '<', '>', "'", '{', '}', ',', '[', ']', '=', '!', '#', '(', ')', '%', '+', '"', ';', '~', '|')
reserved_keywords = {'BEGIN_GROUP', 'BEGIN_OBJECT', 'END', 'END_GROUP', 'END_OBJECT', 'GROUP', 'OBJECT'}
sequence_delimiters = ('(', ')')
set_delimiters = ('{', '}')
spacing_characters = (' ', '\t')
t = '%H:%M:%S.%f'
time_formats = ('%H:%M', '%H:%M:%S', '%H:%M:%S.%f', '%H:%MZ', '%H:%M:%SZ', '%H:%M:%S.%fZ')
true_keyword = 'TRUE'
units_delimiters = ('<', '>')
whitespace = (' ', '\t', '\n', '\r', '\x0b', '\x0c')

pvl.lexer module

Provides lexer functions for PVL.

class pvl.lexer.Preserve[source]

Bases: enum.Enum

An enumeration.

COMMENT = 2
FALSE = 1
NONDECIMAL = 5
QUOTE = 4
UNIT = 3
pvl.lexer.lex_char(char: str, prev_char: str, next_char: str, lexeme: str, preserve: dict, g: pvl.grammar.PVLGrammar, c_info: dict) -> (<class 'str'>, <class 'dict'>)[source]

Returns a modified lexeme string and a modified preserve dict in a two-tuple.

This is the main lexer() helper function for determining how to modify (or not) lexeme and preserve based on the single character in char and the other values passed into this function.

pvl.lexer.lex_comment(char: str, prev_char: str, next_char: str, lexeme: str, preserve: dict, c_info: dict) -> (<class 'str'>, <class 'dict'>)[source]

Returns a modified lexeme string and a modified preserve dict in a two-tuple.

This is a lexer() helper function for determining how to modify lexeme and preserve based on the single character in char which may or may not be a comment character.

This function just makes the decision about whether to call lex_multichar_comments() or lex_singlechar_comments(), and then returns what they return.

pvl.lexer.lex_continue(char: str, next_char: str, lexeme: str, token: pvl.token.Token, preserve: dict, g: pvl.grammar.PVLGrammar) → bool[source]

Return True if accumulation of lexeme should continue based on the values passed into this function, false otherwise.

This is a lexer() helper function.

pvl.lexer.lex_multichar_comments(char: str, prev_char: str, next_char: str, lexeme: str, preserve: dict, comments: (<class 'str'>, <class 'str'>) = (('/*', '*/'), )) -> (<class 'str'>, <class 'dict'>)[source]

Returns a modified lexeme string and a modified preserve dict in a two-tuple.

This is a lexer() helper function for determining how to modify lexeme and preserve based on the single character in char which may or may not be part of a multi-character comment character group.

This function has an internal list of allowed pairs of multi-character comments that it can deal with, if the comments tuple contains any two-tuples that cannot be handled, a NotImplementedError will be raised.

This function will determine whether to append char to lexeme or not, and will set the value of the ‘state’ and ‘end’ values of preserve appropriately.

pvl.lexer.lex_preserve(char: str, lexeme: str, preserve: dict) -> (<class 'str'>, <class 'dict'>)[source]

Returns a modified lexeme string and a modified preserve dict in a two-tuple. The modified lexeme will always be the concatenation of lexeme and char.

This is a lexer() helper function that is responsible for changing the state of the preserve dict, if needed.

If the value for ‘end’ in preserve is the same as char, then the modified preserve will have its ‘state’ value set to Preserve.FALSE and its ‘end’ value set to None, otherwise second item in the returned tuple will be preserve unchanged.

pvl.lexer.lex_singlechar_comments(char: str, lexeme: str, preserve: dict, comments: dict) -> (<class 'str'>, <class 'dict'>)[source]

Returns a modified lexeme string and a modified preserve dict in a two-tuple.

This is a lexer() helper function for determining how to modify lexeme and preserve based on the single character in char which may or may not be a comment character.

If the preserve ‘state’ value is Preserve.COMMENT then the value of lex_preserve() is returned.

If char is among the keys of the comments dict, then the returned lexeme will be the concatenation of lexeme and char. returned preserve dict will have its ‘state’ value set to Preserve.COMMENT and its ‘end’ value set to the value of comments[char].

Otherwise return lexeme and preserve unchanged in the two-tuple.

pvl.lexer.lexer(s: str, g=<pvl.grammar.PVLGrammar object>, d=<pvl.decoder.PVLDecoder object>)[source]

This is a generator function that returns pvl.Token objects based on the passed in string, s, when the generator’s next() is called.

A call to send(t) will ‘return’ the value t to the generator, which will be yielded upon calling next(). This allows a user to ‘peek’ at the next token, but return it if they don’t like what they see.

g is expected to be an instance of pvl.grammar, and d an instance of pvl.decoder. The lexer will perform differently, given different values of g and d.

pvl.new module

pvl.parser module

Parameter Value Language parser.

The definition of PVL used in this module is based on the Consultive Committee for Space Data Systems, and their Parameter Value Language Specification (CCSD0006 and CCSD0008), CCSDS 6441.0-B-2, referred to as the Blue Book with a date of June 2000.

Some of the documention in this module represents the structure diagrams from the Blue Book for parsing PVL in a Backus–Naur form.

So Figure 1-1 from the Blue Book would be represented as :

<Item-A> ::= ( [ <Item-B>+ | <Item-C> ] <Item-D> )*

Finally, the Blue Book defines <WSC> as a possibly empty collection of white space characters or comments:

<WSC> ::= ( <white-space-character> | <comment> )*

However, to help remember that <WSC> could be empty, we will typically always show it as <WSC>*.

Likewise the <Statement-Delimiter> is defined as:

<Statement-Delimiter> ::= <WSC>* [ ‘;’ | <EOF> ]

However, since all elements are optional, we will typically show it as [<Statement-Delimiter>].

The parser deals with managing the tokens that come out of the lexer. Once the parser gets to a state where it has something that needs to be converted to a Python object and returned, it uses the decoder to make that conversion.

Throughout this module, various parser functions will take a tokens: collections.abc.Generator parameter. In all cases, tokens is expected to be a generator iterator which provides pvl.token.Token objects. It should allow for a generated object to be ‘returned’ via the generator’s send() function. When parsing the first object from tokens, if an unexpected object is encountered, it will ‘return’ the object to tokens, and raise a ValueError, so that try-except blocks can be used, and the generator iterator is left in a good state. However, if a parsing anomaly is discovered deeper in parsing a PVL sequence, then a ValueError will be thrown into the tokens generator iterator (via .throw()).

class pvl.parser.EmptyValueAtLine[source]

Bases: str

Empty string to be used as a placeholder for a parameter without a value.

When a label contains a parameter without a value, it is normally considered a broken label in PVL. To allow parsing to continue, we can rectify the broken parameter-value pair by setting the value to have a value of EmptyValueAtLine, which is an empty string (and can be treated as such) with some additional properties.

The argument lineno should be the line number of the error from the original document, which will be available as a property.

Examples::
>>> from pvl.parser import EmptyValueAtLine
>>> EV1 = EmptyValueAtLine(1)
>>> EV1
EmptyValueAtLine(1 does not have a value. Treat as an empty string)
>>> EV1.lineno
1
>>> print(EV1)
<BLANKLINE>
>>> EV1 + 'foo'
'foo'
>>> # Can be turned into an integer and float as 0:
>>> int(EV1)
0
>>> float(EV1)
0.0
class pvl.parser.ODLParser(grammar=None, decoder=None, lexer_fn=None, module_class=<class 'pvl.collections.PVLModule'>, group_class=<class 'pvl.collections.PVLGroup'>, object_class=<class 'pvl.collections.PVLObject'>)[source]

Bases: pvl.parser.PVLParser

A parser based on the rules in the PDS3 Standards Reference (version 3.8, 27 Feb 2009) Chapter 12: Object Description Language Specification and Usage.

It extends PVLParser.

parse_set(tokens: collections.abc.Generator) → set[source]

Overrides the parent function to return the decoded <Set> as a Python set.

The ODL specification only allows scalar_values in Sets, since ODL Sets cannot contain other ODL Sets, an ODL Set can be represented as a Python set (unlike PVL Sets, which must be represented as a Python frozenset objects).

parse_units(value, tokens: collections.abc.Generator) → str[source]

Extends the parent function, since ODL only allows units on numeric values, any others will result in a ValueError.

class pvl.parser.OmniParser(grammar=None, decoder=None, lexer_fn=None, module_class=<class 'pvl.collections.PVLModule'>, group_class=<class 'pvl.collections.PVLGroup'>, object_class=<class 'pvl.collections.PVLObject'>)[source]

Bases: pvl.parser.PVLParser

A permissive PVL/ODL/ISIS label parser that attempts to parse all forms of “PVL” that are thrown at it.

parse(s: str)[source]

Extends the parent function.

If any line ends with a dash (-) followed by a carriage return, form-feed, or newline, plus one or more whitespace characters on the following line, then those characters, and all whitespace characters that begin the next line will be removed.

parse_assignment_statement(tokens: collections.abc.Generator) → tuple[source]

Extends the parent function to allow for more permissive parsing. If an Assignment-Statement is blank, then the value will be assigned an EmptyValueAtLine object.

parse_module_post_hook(module: pvl.collections.MutableMappingSequence, tokens: collections.abc.Generator)[source]

Overrides the parent function to allow for more permissive parsing. If an Assignment-Statement is blank, then the value will be assigned an EmptyValueAtLine object.

parse_value_post_hook(tokens: collections.abc.Generator)[source]

Overrides the parent function to allow for more permissive parsing.

If the next token is a reserved word or delimiter, then it is returned to the tokens and an EmptyValueAtLine object is returned as the value.

class pvl.parser.PVLParser(grammar=None, decoder=None, lexer_fn=None, module_class=<class 'pvl.collections.PVLModule'>, group_class=<class 'pvl.collections.PVLGroup'>, object_class=<class 'pvl.collections.PVLObject'>)[source]

Bases: object

A parser based on the rules in the CCSDS-641.0-B-2 ‘Blue Book’ which defines the PVL language.

Parameters:
  • grammar – A pvl.grammar object, if None or not specified, it will be set to the grammar parameter of decoder (if decoder is not None) or will default to pvl.grammar.OmniGrammar().
  • decoder – defaults to pvl.decoder.OmniDecoder().
  • lexer_fn – must be a lexer function that takes a str, a grammar, and a decoder, as pvl.lexer.lexer() does, which is the default if none is given.
  • module_class – must be a subclass of PVLModule, and is the type of object that will be returned from this parser’s parse() function.
  • group_class – must be a subclass of PVLGroup, and is the type that will be used to hold PVL elements when a PVL Group is encountered during parsing, and must be able to be added to via an .append() function which should take a two-tuple of name and value.
  • object_class – must be a subclass of PVLObject, and is the type that will be used to hold PVL elements when a PVL Object is encountered during parsing, otherwise similar to group_class.
aggregation_cls(begin: str)[source]

Returns an initiated object of the group_class or object_class as specified on this parser’s creation, according to the value of begin. If begin does not match the Group or Object keywords for this parser’s grammar, then it will raise a ValueError.

parse(s: str)[source]

Converts the string, s to a PVLModule.

static parse_WSC_until(token: str, tokens: collections.abc.Generator) → bool[source]

Consumes objects from tokens, if the object’s .is_WSC() function returns True, it will continue until token is encountered and will return True. If it encounters an object that does not meet these conditions, it will ‘return’ that object to tokens and will return False.

tokens is expected to be a generator iterator which provides pvl.token objects.

parse_aggregation_block(tokens: collections.abc.Generator)[source]

Parses the tokens for an Aggregation Block, and returns the modcls object that is the result of the parsing and decoding.

<Aggregation-Block> ::= <Begin-Aggegation-Statement>
(<WSC>* (Assignment-Statement | Aggregation-Block) <WSC>*)+ <End-Aggregation-Statement>

The Begin-Aggregation-Statement Name must match the Block-Name in the paired End-Aggregation-Statement if a Block-Name is present in the End-Aggregation-Statement.

parse_around_equals(tokens: collections.abc.Generator) → None[source]

Parses white space and comments on either side of an equals sign.

tokens is expected to be a generator iterator which provides pvl.token objects.

This is shared functionality for Begin Aggregation Statements and Assignment Statements. It basically covers parsing anything that has a syntax diagram like this:

<WSC>* ‘=’ <WSC>*
parse_assignment_statement(tokens: collections.abc.Generator) → tuple[source]

Parses the tokens for an Assignment Statement.

The returned two-tuple contains the Parameter Name in the first element, and the Value in the second.

<Assignment-Statement> ::= <Parameter-Name> <WSC>* ‘=’ <WSC>*
<Value> [<Statement-Delimiter>]
parse_begin_aggregation_statement(tokens: collections.abc.Generator) → tuple[source]

Parses the tokens for a Begin Aggregation Statement, and returns the name Block Name as a str.

<Begin-Aggregation-Statement-block> ::=
<Begin-Aggegation-Statement> <WSC>* ‘=’ <WSC>* <Block-Name> [<Statement-Delimiter>]

Where <Block-Name> ::= <Parameter-Name>

parse_end_aggregation(begin_agg: str, block_name: str, tokens: collections.abc.Generator) → None[source]

Parses the tokens for an End Aggregation Statement.

<End-Aggregation-Statement-block> ::=
<End-Aggegation-Statement> [<WSC>* ‘=’ <WSC>* <Block-Name>] [<Statement-Delimiter>]

Where <Block-Name> ::= <Parameter-Name>

parse_end_statement(tokens: collections.abc.Generator) → None[source]

Parses the tokens for an End Statement.

<End-Statement> ::= “END” ( <WSC>* | [<Statement-Delimiter>] )

parse_module(tokens: collections.abc.Generator)[source]

Parses the tokens for a PVL Module.

<PVL-Module-Contents> ::=
( <Assignment-Statement> | <WSC>* | <Aggregation-Block> )* [<End-Statement>]
parse_module_post_hook(module: pvl.collections.MutableMappingSequence, tokens: collections.abc.Generator)[source]

This function is meant to be overridden by subclasses that may want to perform some extra processing if ‘normal’ parse_module() operations fail to complete. See OmniParser for an example.

This function shall return a two-tuple, with the first item being the module (altered by processing or unaltered), and the second item being a boolean that will signal whether the tokens should continue to be parsed to accumulate more elements into the returned module, or whether the module is in a good state and should be returned by parse_module().

If the operations within this function are unsuccessful, it should raise an exception (any exception descended from Exception), which will result in the operation of parse_module() as if it were not overridden.

parse_sequence(tokens: collections.abc.Generator) → list[source]

Parses a PVL Sequence.

<Set> ::= “(” <WSC>*
[ <Value> <WSC>* ( “,” <WSC>* <Value> <WSC>* )* ] “)”

Returns the decoded <Sequence> as a Python list.

parse_set(tokens: collections.abc.Generator) → frozenset[source]

Parses a PVL Set.

<Set> ::= “{” <WSC>*
[ <Value> <WSC>* ( “,” <WSC>* <Value> <WSC>* )* ] “}”

Returns the decoded <Set> as a Python frozenset. The PVL specification doesn’t seem to indicate that a PVL Set has distinct values (like a Python set), only that the ordering of the values is unimportant. For now, we will implement PVL Sets as Python frozenset objects.

They are returned as frozenset objects because PVL Sets can contain as their elements other PVL Sets, but since Python set objects are non-hashable, they cannot be members of a set, however, frozenset objects can.

static parse_statement_delimiter(tokens: collections.abc.Generator) → bool[source]

Parses the tokens for a Statement Delimiter.

tokens is expected to be a generator iterator which provides pvl.token objects.

<Statement-Delimiter> ::= <WSC>*
(<white-space-character> | <comment> | ‘;’ | <EOF>)

Although the above structure comes from Figure 2-4 of the Blue Book, the <white-space-character> and <comment> elements are redundant with the presence of [WSC]* so it can be simplified to:

<Statement-Delimiter> ::= <WSC>* [ ‘;’ | <EOF> ]

Typically written [<Statement-Delimiter>].

parse_units(value, tokens: collections.abc.Generator) → str[source]

Parses PVL Units Expression.

<Units-Expression> ::= “<” [<white-space>] <Units-Value>
[<white-space>] “>”

and

<Units-Value> ::= <units-character>
[ [ <units-character> | <white-space> ]*
<units-character> ]

Returns the value and the <Units-Value> as a Units() object.

parse_value(tokens: collections.abc.Generator)[source]

Parses PVL Values.

<Value> ::= (<Simple-Value> | <Set> | <Sequence>)
[<WSC>* <Units Expression>]

Returns the decoded <Value> as an appropriate Python object.

parse_value_post_hook(tokens)[source]

This function is meant to be overridden by subclasses that may want to perform some extra processing if ‘normal’ parse_value() operations fail to yield a value. See OmniParser for an example.

This function shall return an appropriate Python value, similar to what parse_value() would return.

If the operations within this function are unsuccessful, it should raise a ValueError which will result in the operation of parse_value() as if it were not overridden.

pvl.pvl_translate module

A program for converting PVL text to a specific PVL dialect.

The pvl_translate program will read a file with PVL text (any of the kinds of files that pvl.load() reads) or STDIN and will convert that PVL text to a particular PVL dialect. It is not particularly robust, and if it cannot make simple conversions, it will raise errors.

class pvl.pvl_translate.JSONWriter[source]

Bases: pvl.pvl_translate.Writer

dump(dictlike: dict, outfile: os.PathLike)[source]
class pvl.pvl_translate.PVLWriter(encoder)[source]

Bases: pvl.pvl_translate.Writer

dump(dictlike: dict, outfile: os.PathLike)[source]
class pvl.pvl_translate.Writer[source]

Bases: object

Base class for writers. Descendents must implement dump().

dump(dictlike: dict, outfile: os.PathLike)[source]
pvl.pvl_translate.arg_parser(formats)[source]
pvl.pvl_translate.main(argv=None)[source]

pvl.pvl_validate module

A program for testing and validating PVL text.

The pvl_validate program will read a file with PVL text (any of the kinds of files that pvl.load() reads) and will report on which of the various PVL dialects were able to load that PVL text, and then also reports on whether the pvl library can encode the Python Objects back out to PVL text.

You can imagine some PVL text that could be loaded, but is not able to be written out in a particular strict PVL dialect (like PDS3 labels).

pvl.pvl_validate.arg_parser()[source]
pvl.pvl_validate.build_line(elements: list, widths: list, sep=' | ') → str[source]

Returns a string formatted from the elements and widths provided.

pvl.pvl_validate.main(argv=None)[source]
pvl.pvl_validate.pvl_flavor(text, dialect, decenc: dict, filename, verbose=False) -> (<class 'bool'>, <class 'bool'>)[source]

Returns a two-tuple of booleans which indicate whether the text could be loaded and then encoded.

The first boolean in the two-tuple indicates whether the text could be loaded with the given parser, grammar, and decoder. The second indicates whether the loaded PVL object could be encoded with the given encoder, grammar, and decoder. If the first element is False, the second will be None.

pvl.pvl_validate.report(reports: list, flavors: list) → str[source]

Returns a multi-line string which is the pretty-printed report given the list of reports.

pvl.pvl_validate.report_many(r_list: list, flavors: list) → str[source]

Returns a multi-line, table-like string which is the pretty-printed report of the items in r_list.

pvl.token module

class pvl.token.Token(content, grammar=None, decoder=None, pos=0)[source]

Bases: str

A PVL-aware string.

Variables:
  • content – A string that is the Token text.
  • grammar – A pvl.grammar object, if None or not specified, it will be set to the grammar parameter of decoder (if decoder is not None) or will default to PVLGrammar().
  • decoder – A pvl.decoder object, defaults to PVLDecoder(grammar=*grammar*).
  • pos – Integer that describes the starting position of this Token in the source string, defaults to zero.
is_WSC() → bool[source]

Return true if the Token is white space characters or comments according to the Token’s grammar, false otherwise.

is_begin_aggregation() → bool[source]

Return true if the Token is a begin aggregation keyword (e.g. ‘BEGIN_GROUP’ in PVL) according to the Token’s grammar, false otherwise.

is_comment() → bool[source]

Return true if the Token is a comment according to the Token’s grammar (defined as beginning and ending with comment delimieters), false otherwise.

is_datetime() → bool[source]

Return true if the Token’s decoder can convert the Token to a datetime, false otherwise.

Separate is_date() or is_time() functions aren’t needed, since PVL parsing doesn’t distinguish between them. If a user needs that distinction the decoder’s decode_datetime(self) function should return a datetime time, date, or datetime object, as appropriate, and a user can use isinstance() to check.

is_decimal() → bool[source]

Return true if the Token’s decoder can convert the Token to a decimal value, false otherwise.

is_delimiter() → bool[source]

Return true if the Token is a delimiter character (e.g. the ‘;’ in PVL) according to the Token’s grammar, false otherwise.

is_end_statement() → bool[source]

Return true if the Token matches an end statement from its grammar, false otherwise.

is_non_decimal() → bool[source]

Return true if the Token’s decoder can convert the Token to a numeric non-decimal value, false otherwise.

is_numeric() → bool[source]

Return true if the Token’s is_decimal() or is_non_decimal() functions return true, false otherwise.

is_parameter_name() → bool[source]

Return true if the Token is an unquoted string that isn’t a reserved_keyword according to the Token’s grammar, false otherwise.

is_quote() → bool[source]

Return true if the Token is a quote character according to the Token’s grammar, false otherwise.

is_quoted_string() → bool[source]

Return true if the Token can be converted to a quoted string by the Token’s decoder, false otherwise.

is_simple_value() → bool[source]

Return true if the Token’s decoder can convert the Token to a ‘simple value’, however the decoder defines that, false otherwise.

is_space() → bool[source]

Return true if the Token contains whitespace according to the definition of whitespace in the Token’s grammar and there is at least one character, false otherwise.

is_string() → bool[source]

Return true if either the Token’s is_quoted_string() or is_unquoted_string() return true, false otherwise.

is_unquoted_string() → bool[source]

Return false if the Token has any reserved characters, comment characters, whitespace characters or could be interpreted as a number, date, or time according to the Token’s grammar, true otherwise.

isnumeric() → bool[source]

Overrides str.isnumeric() to be the same as Token’s is_numeric() function, so that we don’t get inconsisent behavior if someone forgets an underbar.

isspace() → bool[source]

Overrides str.isspace() to be the same as Token’s is_space() function, so that we don’t get inconsisent behavior if someone forgets an underbar.

lstrip(chars=None)[source]

Extends str.lstrip() to strip whitespace according to the definition of whitespace in the Token’s grammar instead of the default Python whitespace definition.

replace(*args)[source]

Extends str.replace() to return a Token.

rstrip(chars=None)[source]

Extends str.rstrip() to strip whitespace according to the definition of whitespace in the Token’s grammar instead of the default Python whitespace definition.

split(sep=None, maxsplit=-1) → list[source]

Extends str.split() that calling split() on a Token returns a list of Tokens.

strip(chars=None)[source]

Extends str.strip() to strip whitespace according to the definition of whitespace in the Token’s grammar instead of the default Python whitespace definition.

Module contents

Python implementation of PVL (Parameter Value Language).

pvl.load(path, parser=None, grammar=None, decoder=None, encoding=None, **kwargs)[source]

Returns a Python object from parsing the file at path.

Parameters:

If path is not an os.PathLike, it will be assumed to be an already-opened file object, and .read() will be applied to extract the text.

If the os.PathLike or file object contains some bytes decodable as text, followed by some that is not (e.g. an ISIS cube file), that’s fine, this function will just extract the decodable text.

pvl.loads(s: str, parser=None, grammar=None, decoder=None, **kwargs)[source]

Deserialize the string, s, as a Python object.

Parameters:
pvl.dump(module, path, **kwargs)[source]

Serialize module as PVL text to the provided path.

Parameters:
  • module – a PVLModule or dict-like object to serialize.
  • path – an os.PathLike
  • **kwargs – the keyword arguments to pass to dumps().

If path is an os.PathLike, it will attempt to be opened and the serialized module will be written into that file via the pathlib.Path.write_text() function, and will return what that function returns.

If path is not an os.PathLike, it will be assumed to be an already-opened file object, and .write() will be applied on that object to write the serialized module, and will return what that function returns.

pvl.dumps(module, encoder=None, grammar=None, decoder=None, **kwargs) → str[source]

Returns a string where the module object has been serialized to PVL syntax.

Parameters:
  • module – a PVLModule or dict like object to serialize.
  • encoder – defaults to pvl.parser.PDSLabelEncoder().
  • grammar – defaults to pvl.grammar.ODLGrammar().
  • decoder – defaults to pvl.decoder.ODLDecoder().
  • **kwargs – the keyword arguments to pass to the encoder class if encoder is none.
class pvl.PVLModule(*args, **kwargs)[source]

Bases: pvl.collections.OrderedMultiDict

class pvl.PVLGroup(*args, **kwargs)[source]

Bases: pvl.collections.PVLAggregation

class pvl.PVLObject(*args, **kwargs)[source]

Bases: pvl.collections.PVLAggregation

class pvl.Quantity[source]

Bases: pvl.collections.Quantity

A simple collections.namedtuple object to contain a value and units parameter.

If you need more comprehensive units handling, you may want to use the astropy.units.Quantity object, the pint.Quantity object, or some other 3rd party object. Please see the documentation on Quantities: Values and Units for how to use 3rd party Quantity objects with pvl.

class pvl.Units[source]

Bases: pvl.collections.Quantity