asdf-format / asdf-coordinates-schemas

ASDF schemas for coordinates
https://asdf-coordinates-schemas.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
1 stars 3 forks source link

Test failure with ICRS() missing 1 required positional argument 'dec' #59

Closed bnavigator closed 7 months ago

bnavigator commented 8 months ago

The new release fails with an example test and current release versions of astropy and asdf:

[  205s] E                   TypeError: ICRS() missing 1 required positional argument: 'dec' (or first argument should be an instance of UnitSphericalRepresentation).
[  205s] 
[  205s] /usr/lib64/python3.9/site-packages/astropy/coordinates/representation/base.py:191: TypeError
[  205s] ----------------------------- Captured stdout call -----------------------------
[  205s] Example: A Galactocentric frame without data
[  205s]  From file: /home/abuild/rpmbuild/BUILD/asdf_coordinates_schemas-0.3.0/resources/schemas/frames/galactocentric-1.1.0.yaml
[  205s] =========================== short test summary info ============================
[  205s] FAILED resources/schemas/frames/galactocentric-1.1.0.yaml::test_example_0 - T...
[  205s] ================== 1 failed, 123 passed in 171.64s (0:02:51) ===================
python39-asdf-3.1.0
python39-asdf-standard-1.1.1
python39-asdf-astropy-0.5.0
python39-asdf-coordinates-schemas-0.3.0
python39-astropy-6.0.0
pytest output ```python [ 33s] platform linux -- Python 3.9.18, pytest-7.4.4, pluggy-1.3.0 -- /usr/bin/python3.9 [ 33s] cachedir: .pytest_cache [ 33s] rootdir: /home/abuild/rpmbuild/BUILD/asdf_coordinates_schemas-0.3.0 [ 33s] configfile: pyproject.toml [ 33s] testpaths: tests, resources [ 33s] plugins: asdf-3.1.0 [ 33s] collecting ... collected 124 items ... [ 205s] =================================== FAILURES =================================== [ 205s] _________________________________ test session _________________________________ [ 205s] [ 205s] self = <[AttributeError("'ICRS' object has no attribute '_data'") raised in repr()] ICRS object at 0x7fcaec233e20> [ 205s] args = [], copy = True, kwargs = {} [ 205s] [ 205s] def _infer_data(self, args, copy, kwargs): ... [ 205s] [ 205s] > tree = yamlutil.tagged_tree_to_custom_tree(tree, self, _force_raw_types) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/_asdf.py:889: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] tree = {'example': {'frame_attributes': {'galcen_coord': {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}...5}}, 'type': 'CartesianDifferential'}, 'roll': {'unit': 'deg', 'value': 0.0}, 'z_sun': {'unit': 'pc', 'value': 27.0}}}} [ 205s] ctx = , force_raw_types = False [ 205s] _serialization_context = [ 205s] [ 205s] def tagged_tree_to_custom_tree(tree, ctx, force_raw_types=False, _serialization_context=None): [ 205s] """ [ 205s] Convert a tree containing only basic data types, annotated with [ 205s] tags, to a tree containing custom data types. [ 205s] """ [ 205s] if _serialization_context is None: [ 205s] _serialization_context = ctx._create_serialization_context(BlockAccess.READ) [ 205s] [ 205s] extension_manager = _serialization_context.extension_manager [ 205s] [ 205s] def _walker(node): [ 205s] if force_raw_types: [ 205s] return node [ 205s] [ 205s] tag = getattr(node, "_tag", None) [ 205s] if tag is None: [ 205s] return node [ 205s] [ 205s] if extension_manager.handles_tag(tag): [ 205s] converter = extension_manager.get_converter_for_tag(tag) [ 205s] obj = converter.from_yaml_tree(node.data, tag, _serialization_context) [ 205s] _serialization_context.assign_object(obj) [ 205s] _serialization_context.assign_blocks() [ 205s] _serialization_context._mark_extension_used(converter.extension) [ 205s] return obj [ 205s] [ 205s] if not ctx._ignore_unrecognized_tag: [ 205s] warnings.warn( [ 205s] f"{tag} is not recognized, converting to raw Python data structure", [ 205s] AsdfConversionWarning, [ 205s] ) [ 205s] return node [ 205s] [ 205s] > return treeutil.walk_and_modify( [ 205s] tree, [ 205s] _walker, [ 205s] ignore_implicit_conversion=ctx._ignore_implicit_conversion, [ 205s] # Walk the tree in postorder, so that extensions receive [ 205s] # container nodes with children already deserialized. [ 205s] postorder=True, [ 205s] _context=ctx._tree_modification_context, [ 205s] ) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/yamlutil.py:339: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] top = {'example': {'frame_attributes': {'galcen_coord': {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}...5}}, 'type': 'CartesianDifferential'}, 'roll': {'unit': 'deg', 'value': 0.0}, 'z_sun': {'unit': 'pc', 'value': 27.0}}}} [ 205s] callback = ._walker at 0x7fcaead07c10> [ 205s] ignore_implicit_conversion = False, postorder = True [ 205s] _context = [ 205s] [ 205s] def walk_and_modify(top, callback, ignore_implicit_conversion=False, postorder=True, _context=None): [ 205s] """Modify a tree by walking it with a callback function. It also has [ 205s] the effect of doing a deep copy. [ 205s] [ 205s] Parameters [ 205s] ---------- [ 205s] top : object [ 205s] The root of the tree. May be a dict, list or other Python object. [ 205s] [ 205s] callback : callable [ 205s] A function to call at each node in the tree. It takes either [ 205s] one or two arguments: [ 205s] [ 205s] - an instance from the tree [ 205s] - a json id (optional) [ 205s] [ 205s] It may return a different instance in order to modify the [ 205s] tree. If the singleton instance `~asdf.treeutil._RemoveNode` [ 205s] is returned, the node will be removed from the tree. [ 205s] [ 205s] The json id is the context under which any relative URLs [ 205s] should be resolved. It may be `None` if no ids are in the file [ 205s] [ 205s] The tree is traversed depth-first, with order specified by the [ 205s] ``postorder`` argument. [ 205s] [ 205s] postorder : bool [ 205s] Determines the order in which the callable is invoked on nodes of [ 205s] the tree. If `True`, the callable will be invoked on children [ 205s] before their parents. If `False`, the callable is invoked on the [ 205s] parents first. Defaults to `True`. [ 205s] [ 205s] ignore_implicit_conversion : bool [ 205s] Controls whether warnings should be issued when implicitly converting a [ 205s] given type instance in the tree into a serializable object. The primary [ 205s] case for this is currently ``namedtuple``. [ 205s] [ 205s] Defaults to `False`. [ 205s] [ 205s] Returns [ 205s] ------- [ 205s] tree : object [ 205s] The modified tree. [ 205s] [ 205s] """ [ 205s] callback_arity = callback.__code__.co_argcount [ 205s] if callback_arity < 1 or callback_arity > 2: [ 205s] msg = "Expected callback to accept one or two arguments" [ 205s] raise ValueError(msg) [ 205s] [ 205s] def _handle_generator(result): [ 205s] # If the result is a generator, generate one value to [ 205s] # extract the true result, then register the generator [ 205s] # to be drained later. [ 205s] if isinstance(result, types.GeneratorType): [ 205s] generator = result [ 205s] result = next(generator) [ 205s] _context.add_generator(generator) [ 205s] [ 205s] return result [ 205s] [ 205s] def _handle_callback(node, json_id): [ 205s] result = callback(node) if callback_arity == 1 else callback(node, json_id) [ 205s] [ 205s] return _handle_generator(result) [ 205s] [ 205s] def _handle_mapping(node, json_id): [ 205s] result = node.__class__() [ 205s] if isinstance(node, tagged.Tagged): [ 205s] result._tag = node._tag [ 205s] [ 205s] pending_items = {} [ 205s] for key, value in node.items(): [ 205s] if _context.is_pending(value): [ 205s] # The child node is pending modification, which means [ 205s] # it must be its own ancestor. Assign the special [ 205s] # PendingValue instance for now, and note that we'll [ 205s] # need to fill in the real value later. [ 205s] pending_items[key] = value [ 205s] result[key] = PendingValue [ 205s] [ 205s] elif (val := _recurse(value, json_id)) is not RemoveNode: [ 205s] result[key] = val [ 205s] [ 205s] yield result [ 205s] [ 205s] if len(pending_items) > 0: [ 205s] # Now that we've yielded, the pending children should [ 205s] # be available. [ 205s] for key, value in pending_items.items(): [ 205s] if (val := _recurse(value, json_id)) is not RemoveNode: [ 205s] result[key] = val [ 205s] else: [ 205s] # The callback may have decided to delete [ 205s] # this node after all. [ 205s] del result[key] [ 205s] [ 205s] def _handle_mutable_sequence(node, json_id): [ 205s] result = node.__class__() [ 205s] if isinstance(node, tagged.Tagged): [ 205s] result._tag = node._tag [ 205s] [ 205s] pending_items = {} [ 205s] for i, value in enumerate(node): [ 205s] if _context.is_pending(value): [ 205s] # The child node is pending modification, which means [ 205s] # it must be its own ancestor. Assign the special [ 205s] # PendingValue instance for now, and note that we'll [ 205s] # need to fill in the real value later. [ 205s] pending_items[i] = value [ 205s] result.append(PendingValue) [ 205s] else: [ 205s] result.append(_recurse(value, json_id)) [ 205s] [ 205s] yield result [ 205s] [ 205s] for i, value in pending_items.items(): [ 205s] # Now that we've yielded, the pending children should [ 205s] # be available. [ 205s] result[i] = _recurse(value, json_id) [ 205s] [ 205s] def _handle_immutable_sequence(node, json_id): [ 205s] # Immutable sequences containing themselves are impossible [ 205s] # to construct (well, maybe possible in a C extension, but [ 205s] # we're not going to worry about that), so we don't need [ 205s] # to yield here. [ 205s] contents = [_recurse(value, json_id) for value in node] [ 205s] [ 205s] try: [ 205s] result = node.__class__(contents) [ 205s] if isinstance(node, tagged.Tagged): [ 205s] result._tag = node._tag [ 205s] except TypeError: [ 205s] # The derived class signature is different, so simply store the [ 205s] # list representing the contents. Currently this is primarily [ 205s] # intended to handle namedtuple and NamedTuple instances. [ 205s] if not ignore_implicit_conversion: [ 205s] warnings.warn(f"Failed to serialize instance of {type(node)}, converting to list instead", AsdfWarning) [ 205s] result = contents [ 205s] [ 205s] return result [ 205s] [ 205s] def _handle_children(node, json_id): [ 205s] if isinstance(node, dict): [ 205s] result = _handle_mapping(node, json_id) [ 205s] elif isinstance(node, tuple): [ 205s] result = _handle_immutable_sequence(node, json_id) [ 205s] elif isinstance(node, list): [ 205s] result = _handle_mutable_sequence(node, json_id) [ 205s] else: [ 205s] result = node [ 205s] [ 205s] return _handle_generator(result) [ 205s] [ 205s] def _recurse(node, json_id=None): [ 205s] if node in _context: [ 205s] # The node's modified result has already been [ 205s] # created, all we need to do is return it. This [ 205s] # occurs when the tree contains multiple references [ 205s] # to the same object id. [ 205s] return _context[node] [ 205s] [ 205s] # Inform the context that we're going to start modifying [ 205s] # this node. [ 205s] with _context.pending(node): [ 205s] # Take note of the "id" field, in case we're modifying [ 205s] # a schema and need to know the namespace for resolving [ 205s] # URIs. Ignore an id that is not a string, since it may [ 205s] # be an object defining an id property and not an id [ 205s] # itself (this is common in metaschemas). [ 205s] if isinstance(node, dict) and "id" in node and isinstance(node["id"], str): [ 205s] json_id = node["id"] [ 205s] [ 205s] if postorder: [ 205s] # If this is a postorder modification, invoke the [ 205s] # callback on this node's children first. [ 205s] result = _handle_children(node, json_id) [ 205s] result = _handle_callback(result, json_id) [ 205s] else: [ 205s] # Otherwise, invoke the callback on the node first, [ 205s] # then its children. [ 205s] result = _handle_callback(node, json_id) [ 205s] result = _handle_children(result, json_id) [ 205s] [ 205s] # Store the result in the context, in case there are [ 205s] # additional references to the same node elsewhere in [ 205s] # the tree. [ 205s] _context[node] = result [ 205s] [ 205s] return result [ 205s] [ 205s] if _context is None: [ 205s] _context = _TreeModificationContext() [ 205s] [ 205s] with _context: [ 205s] > return _recurse(top) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:418: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'example': {'frame_attributes': {'galcen_coord': {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}...5}}, 'type': 'CartesianDifferential'}, 'roll': {'unit': 'deg', 'value': 0.0}, 'z_sun': {'unit': 'pc', 'value': 27.0}}}} [ 205s] json_id = None [ 205s] [ 205s] def _recurse(node, json_id=None): [ 205s] if node in _context: [ 205s] # The node's modified result has already been [ 205s] # created, all we need to do is return it. This [ 205s] # occurs when the tree contains multiple references [ 205s] # to the same object id. [ 205s] return _context[node] [ 205s] [ 205s] # Inform the context that we're going to start modifying [ 205s] # this node. [ 205s] with _context.pending(node): [ 205s] # Take note of the "id" field, in case we're modifying [ 205s] # a schema and need to know the namespace for resolving [ 205s] # URIs. Ignore an id that is not a string, since it may [ 205s] # be an object defining an id property and not an id [ 205s] # itself (this is common in metaschemas). [ 205s] if isinstance(node, dict) and "id" in node and isinstance(node["id"], str): [ 205s] json_id = node["id"] [ 205s] [ 205s] if postorder: [ 205s] # If this is a postorder modification, invoke the [ 205s] # callback on this node's children first. [ 205s] > result = _handle_children(node, json_id) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:399: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'example': {'frame_attributes': {'galcen_coord': {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}...5}}, 'type': 'CartesianDifferential'}, 'roll': {'unit': 'deg', 'value': 0.0}, 'z_sun': {'unit': 'pc', 'value': 27.0}}}} [ 205s] json_id = None [ 205s] [ 205s] def _handle_children(node, json_id): [ 205s] if isinstance(node, dict): [ 205s] result = _handle_mapping(node, json_id) [ 205s] elif isinstance(node, tuple): [ 205s] result = _handle_immutable_sequence(node, json_id) [ 205s] elif isinstance(node, list): [ 205s] result = _handle_mutable_sequence(node, json_id) [ 205s] else: [ 205s] result = node [ 205s] [ 205s] > return _handle_generator(result) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:375: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] result = ._handle_mapping at 0x7fcaed080eb0> [ 205s] [ 205s] def _handle_generator(result): [ 205s] # If the result is a generator, generate one value to [ 205s] # extract the true result, then register the generator [ 205s] # to be drained later. [ 205s] if isinstance(result, types.GeneratorType): [ 205s] generator = result [ 205s] > result = next(generator) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:279: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'example': {'frame_attributes': {'galcen_coord': {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}...5}}, 'type': 'CartesianDifferential'}, 'roll': {'unit': 'deg', 'value': 0.0}, 'z_sun': {'unit': 'pc', 'value': 27.0}}}} [ 205s] json_id = None [ 205s] [ 205s] def _handle_mapping(node, json_id): [ 205s] result = node.__class__() [ 205s] if isinstance(node, tagged.Tagged): [ 205s] result._tag = node._tag [ 205s] [ 205s] pending_items = {} [ 205s] for key, value in node.items(): [ 205s] if _context.is_pending(value): [ 205s] # The child node is pending modification, which means [ 205s] # it must be its own ancestor. Assign the special [ 205s] # PendingValue instance for now, and note that we'll [ 205s] # need to fill in the real value later. [ 205s] pending_items[key] = value [ 205s] result[key] = PendingValue [ 205s] [ 205s] > elif (val := _recurse(value, json_id)) is not RemoveNode: [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:304: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'frame_attributes': {'galcen_coord': {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'u...25}}, 'type': 'CartesianDifferential'}, 'roll': {'unit': 'deg', 'value': 0.0}, 'z_sun': {'unit': 'pc', 'value': 27.0}}} [ 205s] json_id = None [ 205s] [ 205s] def _recurse(node, json_id=None): [ 205s] if node in _context: [ 205s] # The node's modified result has already been [ 205s] # created, all we need to do is return it. This [ 205s] # occurs when the tree contains multiple references [ 205s] # to the same object id. [ 205s] return _context[node] [ 205s] [ 205s] # Inform the context that we're going to start modifying [ 205s] # this node. [ 205s] with _context.pending(node): [ 205s] # Take note of the "id" field, in case we're modifying [ 205s] # a schema and need to know the namespace for resolving [ 205s] # URIs. Ignore an id that is not a string, since it may [ 205s] # be an object defining an id property and not an id [ 205s] # itself (this is common in metaschemas). [ 205s] if isinstance(node, dict) and "id" in node and isinstance(node["id"], str): [ 205s] json_id = node["id"] [ 205s] [ 205s] if postorder: [ 205s] # If this is a postorder modification, invoke the [ 205s] # callback on this node's children first. [ 205s] > result = _handle_children(node, json_id) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:399: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'frame_attributes': {'galcen_coord': {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'u...25}}, 'type': 'CartesianDifferential'}, 'roll': {'unit': 'deg', 'value': 0.0}, 'z_sun': {'unit': 'pc', 'value': 27.0}}} [ 205s] json_id = None [ 205s] [ 205s] def _handle_children(node, json_id): [ 205s] if isinstance(node, dict): [ 205s] result = _handle_mapping(node, json_id) [ 205s] elif isinstance(node, tuple): [ 205s] result = _handle_immutable_sequence(node, json_id) [ 205s] elif isinstance(node, list): [ 205s] result = _handle_mutable_sequence(node, json_id) [ 205s] else: [ 205s] result = node [ 205s] [ 205s] > return _handle_generator(result) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:375: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] result = ._handle_mapping at 0x7fcaecf696d0> [ 205s] [ 205s] def _handle_generator(result): [ 205s] # If the result is a generator, generate one value to [ 205s] # extract the true result, then register the generator [ 205s] # to be drained later. [ 205s] if isinstance(result, types.GeneratorType): [ 205s] generator = result [ 205s] > result = next(generator) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:279: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'frame_attributes': {'galcen_coord': {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'u...25}}, 'type': 'CartesianDifferential'}, 'roll': {'unit': 'deg', 'value': 0.0}, 'z_sun': {'unit': 'pc', 'value': 27.0}}} [ 205s] json_id = None [ 205s] [ 205s] def _handle_mapping(node, json_id): [ 205s] result = node.__class__() [ 205s] if isinstance(node, tagged.Tagged): [ 205s] result._tag = node._tag [ 205s] [ 205s] pending_items = {} [ 205s] for key, value in node.items(): [ 205s] if _context.is_pending(value): [ 205s] # The child node is pending modification, which means [ 205s] # it must be its own ancestor. Assign the special [ 205s] # PendingValue instance for now, and note that we'll [ 205s] # need to fill in the real value later. [ 205s] pending_items[key] = value [ 205s] result[key] = PendingValue [ 205s] [ 205s] > elif (val := _recurse(value, json_id)) is not RemoveNode: [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:304: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'galcen_coord': {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value':...d_z': {'unit': 'km s-1', 'value': 7.25}}, 'type': 'CartesianDifferential'}, 'roll': {'unit': 'deg', 'value': 0.0}, ...} [ 205s] json_id = None [ 205s] [ 205s] def _recurse(node, json_id=None): [ 205s] if node in _context: [ 205s] # The node's modified result has already been [ 205s] # created, all we need to do is return it. This [ 205s] # occurs when the tree contains multiple references [ 205s] # to the same object id. [ 205s] return _context[node] [ 205s] [ 205s] # Inform the context that we're going to start modifying [ 205s] # this node. [ 205s] with _context.pending(node): [ 205s] # Take note of the "id" field, in case we're modifying [ 205s] # a schema and need to know the namespace for resolving [ 205s] # URIs. Ignore an id that is not a string, since it may [ 205s] # be an object defining an id property and not an id [ 205s] # itself (this is common in metaschemas). [ 205s] if isinstance(node, dict) and "id" in node and isinstance(node["id"], str): [ 205s] json_id = node["id"] [ 205s] [ 205s] if postorder: [ 205s] # If this is a postorder modification, invoke the [ 205s] # callback on this node's children first. [ 205s] > result = _handle_children(node, json_id) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:399: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'galcen_coord': {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value':...d_z': {'unit': 'km s-1', 'value': 7.25}}, 'type': 'CartesianDifferential'}, 'roll': {'unit': 'deg', 'value': 0.0}, ...} [ 205s] json_id = None [ 205s] [ 205s] def _handle_children(node, json_id): [ 205s] if isinstance(node, dict): [ 205s] result = _handle_mapping(node, json_id) [ 205s] elif isinstance(node, tuple): [ 205s] result = _handle_immutable_sequence(node, json_id) [ 205s] elif isinstance(node, list): [ 205s] result = _handle_mutable_sequence(node, json_id) [ 205s] else: [ 205s] result = node [ 205s] [ 205s] > return _handle_generator(result) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:375: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] result = ._handle_mapping at 0x7fcaee03c270> [ 205s] [ 205s] def _handle_generator(result): [ 205s] # If the result is a generator, generate one value to [ 205s] # extract the true result, then register the generator [ 205s] # to be drained later. [ 205s] if isinstance(result, types.GeneratorType): [ 205s] generator = result [ 205s] > result = next(generator) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:279: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'galcen_coord': {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value':...d_z': {'unit': 'km s-1', 'value': 7.25}}, 'type': 'CartesianDifferential'}, 'roll': {'unit': 'deg', 'value': 0.0}, ...} [ 205s] json_id = None [ 205s] [ 205s] def _handle_mapping(node, json_id): [ 205s] result = node.__class__() [ 205s] if isinstance(node, tagged.Tagged): [ 205s] result._tag = node._tag [ 205s] [ 205s] pending_items = {} [ 205s] for key, value in node.items(): [ 205s] if _context.is_pending(value): [ 205s] # The child node is pending modification, which means [ 205s] # it must be its own ancestor. Assign the special [ 205s] # PendingValue instance for now, and note that we'll [ 205s] # need to fill in the real value later. [ 205s] pending_items[key] = value [ 205s] result[key] = PendingValue [ 205s] [ 205s] > elif (val := _recurse(value, json_id)) is not RemoveNode: [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:304: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value': 266.4051, 'wrap_angle': {'unit': 'deg', 'value': 360.0}}}, 'type': 'UnitSphericalRepresentation'}, 'frame_attributes': {}} [ 205s] json_id = None [ 205s] [ 205s] def _recurse(node, json_id=None): [ 205s] if node in _context: [ 205s] # The node's modified result has already been [ 205s] # created, all we need to do is return it. This [ 205s] # occurs when the tree contains multiple references [ 205s] # to the same object id. [ 205s] return _context[node] [ 205s] [ 205s] # Inform the context that we're going to start modifying [ 205s] # this node. [ 205s] with _context.pending(node): [ 205s] # Take note of the "id" field, in case we're modifying [ 205s] # a schema and need to know the namespace for resolving [ 205s] # URIs. Ignore an id that is not a string, since it may [ 205s] # be an object defining an id property and not an id [ 205s] # itself (this is common in metaschemas). [ 205s] if isinstance(node, dict) and "id" in node and isinstance(node["id"], str): [ 205s] json_id = node["id"] [ 205s] [ 205s] if postorder: [ 205s] # If this is a postorder modification, invoke the [ 205s] # callback on this node's children first. [ 205s] result = _handle_children(node, json_id) [ 205s] > result = _handle_callback(result, json_id) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:400: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value': 266.4051, 'wrap_angle': {'unit': 'deg', 'value': 360.0}}}, 'type': 'UnitSphericalRepresentation'}, 'frame_attributes': {}} [ 205s] json_id = None [ 205s] [ 205s] def _handle_callback(node, json_id): [ 205s] > result = callback(node) if callback_arity == 1 else callback(node, json_id) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/treeutil.py:285: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] node = {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value': 266.4051, 'wrap_angle': {'unit': 'deg', 'value': 360.0}}}, 'type': 'UnitSphericalRepresentation'}, 'frame_attributes': {}} [ 205s] [ 205s] def _walker(node): [ 205s] if force_raw_types: [ 205s] return node [ 205s] [ 205s] tag = getattr(node, "_tag", None) [ 205s] if tag is None: [ 205s] return node [ 205s] [ 205s] if extension_manager.handles_tag(tag): [ 205s] converter = extension_manager.get_converter_for_tag(tag) [ 205s] > obj = converter.from_yaml_tree(node.data, tag, _serialization_context) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/yamlutil.py:326: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] self = [ 205s] node = {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value': 266.4051, 'wrap_angle': {'unit': 'deg', 'value': 360.0}}}, 'type': 'UnitSphericalRepresentation'}, 'frame_attributes': {}} [ 205s] tag = 'tag:astropy.org:astropy/coordinates/frames/icrs-1.1.0' [ 205s] ctx = [ 205s] [ 205s] def from_yaml_tree(self, node, tag, ctx): [ 205s] """ [ 205s] Convert a YAML node into an instance of a custom type. [ 205s] [ 205s] Parameters [ 205s] ---------- [ 205s] tree : dict or list or str [ 205s] The YAML node to convert. [ 205s] tag : str [ 205s] The YAML tag of the object being converted. [ 205s] ctx : asdf.extension.SerializationContext [ 205s] Serialization parameters. [ 205s] [ 205s] Returns [ 205s] ------- [ 205s] object [ 205s] """ [ 205s] > return self._delegate.from_yaml_tree(node, tag, ctx) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf/extension/_converter.py:291: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] self = [ 205s] node = {'data': {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value': 266.4051, 'wrap_angle': {'unit': 'deg', 'value': 360.0}}}, 'type': 'UnitSphericalRepresentation'}, 'frame_attributes': {}} [ 205s] tag = 'tag:astropy.org:astropy/coordinates/frames/icrs-1.1.0' [ 205s] ctx = [ 205s] [ 205s] def from_yaml_tree(self, node, tag, ctx): [ 205s] data = node.get("data", None) [ 205s] if data is not None: [ 205s] > return self.frame_type(node["data"], **node["frame_attributes"]) [ 205s] [ 205s] /usr/lib/python3.9/site-packages/asdf_astropy/converters/coordinates/frame.py:51: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] self = <[AttributeError("'ICRS' object has no attribute '_data'") raised in repr()] ICRS object at 0x7fcaec233e20> [ 205s] copy = True, representation_type = None, differential_type = None [ 205s] args = ({'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value': 266.4051, 'wrap_angle': {'unit': 'deg', 'value': 360.0}}}, 'type': 'UnitSphericalRepresentation'},) [ 205s] kwargs = {} [ 205s] [ 205s] def __init__( [ 205s] self, [ 205s] *args, [ 205s] copy=True, [ 205s] representation_type=None, [ 205s] differential_type=None, [ 205s] **kwargs, [ 205s] ): [ 205s] self._attr_names_with_defaults = [] [ 205s] [ 205s] self._representation = self._infer_representation( [ 205s] representation_type, differential_type [ 205s] ) [ 205s] > data = self._infer_data(args, copy, kwargs) # possibly None. [ 205s] [ 205s] /usr/lib64/python3.9/site-packages/astropy/coordinates/baseframe.py:310: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] self = <[AttributeError("'ICRS' object has no attribute '_data'") raised in repr()] ICRS object at 0x7fcaec233e20> [ 205s] args = [], copy = True, kwargs = {} [ 205s] [ 205s] def _infer_data(self, args, copy, kwargs): [ 205s] # if not set below, this is a frame with no data [ 205s] representation_data = None [ 205s] differential_data = None [ 205s] [ 205s] args = list(args) # need to be able to pop them [ 205s] if args and (isinstance(args[0], r.BaseRepresentation) or args[0] is None): [ 205s] representation_data = args.pop(0) # This can still be None [ 205s] if len(args) > 0: [ 205s] raise TypeError( [ 205s] "Cannot create a frame with both a representation object " [ 205s] "and other positional arguments" [ 205s] ) [ 205s] [ 205s] if representation_data is not None: [ 205s] diffs = representation_data.differentials [ 205s] differential_data = diffs.get("s", None) [ 205s] if (differential_data is None and len(diffs) > 0) or ( [ 205s] differential_data is not None and len(diffs) > 1 [ 205s] ): [ 205s] raise ValueError( [ 205s] "Multiple differentials are associated with the representation" [ 205s] " object passed in to the frame initializer. Only a single" [ 205s] f" velocity differential is supported. Got: {diffs}" [ 205s] ) [ 205s] [ 205s] else: [ 205s] representation_cls = self.get_representation_cls() [ 205s] # Get any representation data passed in to the frame initializer [ 205s] # using keyword or positional arguments for the component names [ 205s] repr_kwargs = {} [ 205s] for nmkw, nmrep in self.representation_component_names.items(): [ 205s] if len(args) > 0: [ 205s] # first gather up positional args [ 205s] repr_kwargs[nmrep] = args.pop(0) [ 205s] elif nmkw in kwargs: [ 205s] repr_kwargs[nmrep] = kwargs.pop(nmkw) [ 205s] [ 205s] # special-case the Spherical->UnitSpherical if no `distance` [ 205s] [ 205s] if repr_kwargs: [ 205s] # TODO: determine how to get rid of the part before the "try" - [ 205s] # currently removing it has a performance regression for [ 205s] # unitspherical because of the try-related overhead. [ 205s] # Also frames have no way to indicate what the "distance" is [ 205s] if repr_kwargs.get("distance", True) is None: [ 205s] del repr_kwargs["distance"] [ 205s] [ 205s] if ( [ 205s] issubclass(representation_cls, r.SphericalRepresentation) [ 205s] and "distance" not in repr_kwargs [ 205s] ): [ 205s] representation_cls = representation_cls._unit_representation [ 205s] [ 205s] try: [ 205s] representation_data = representation_cls(copy=copy, **repr_kwargs) [ 205s] except TypeError as e: [ 205s] # this except clause is here to make the names of the [ 205s] # attributes more human-readable. Without this the names [ 205s] # come from the representation instead of the frame's [ 205s] # attribute names. [ 205s] try: [ 205s] representation_data = representation_cls._unit_representation( [ 205s] copy=copy, **repr_kwargs [ 205s] ) [ 205s] except Exception: [ 205s] msg = str(e) [ 205s] names = self.get_representation_component_names() [ 205s] for frame_name, repr_name in names.items(): [ 205s] msg = msg.replace(repr_name, frame_name) [ 205s] msg = msg.replace("__init__()", f"{self.__class__.__name__}()") [ 205s] e.args = (msg,) [ 205s] > raise e [ 205s] [ 205s] /usr/lib64/python3.9/site-packages/astropy/coordinates/baseframe.py:473: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] self = <[AttributeError("'ICRS' object has no attribute '_data'") raised in repr()] ICRS object at 0x7fcaec233e20> [ 205s] args = [], copy = True, kwargs = {} [ 205s] [ 205s] def _infer_data(self, args, copy, kwargs): [ 205s] # if not set below, this is a frame with no data [ 205s] representation_data = None [ 205s] differential_data = None [ 205s] [ 205s] args = list(args) # need to be able to pop them [ 205s] if args and (isinstance(args[0], r.BaseRepresentation) or args[0] is None): [ 205s] representation_data = args.pop(0) # This can still be None [ 205s] if len(args) > 0: [ 205s] raise TypeError( [ 205s] "Cannot create a frame with both a representation object " [ 205s] "and other positional arguments" [ 205s] ) [ 205s] [ 205s] if representation_data is not None: [ 205s] diffs = representation_data.differentials [ 205s] differential_data = diffs.get("s", None) [ 205s] if (differential_data is None and len(diffs) > 0) or ( [ 205s] differential_data is not None and len(diffs) > 1 [ 205s] ): [ 205s] raise ValueError( [ 205s] "Multiple differentials are associated with the representation" [ 205s] " object passed in to the frame initializer. Only a single" [ 205s] f" velocity differential is supported. Got: {diffs}" [ 205s] ) [ 205s] [ 205s] else: [ 205s] representation_cls = self.get_representation_cls() [ 205s] # Get any representation data passed in to the frame initializer [ 205s] # using keyword or positional arguments for the component names [ 205s] repr_kwargs = {} [ 205s] for nmkw, nmrep in self.representation_component_names.items(): [ 205s] if len(args) > 0: [ 205s] # first gather up positional args [ 205s] repr_kwargs[nmrep] = args.pop(0) [ 205s] elif nmkw in kwargs: [ 205s] repr_kwargs[nmrep] = kwargs.pop(nmkw) [ 205s] [ 205s] # special-case the Spherical->UnitSpherical if no `distance` [ 205s] [ 205s] if repr_kwargs: [ 205s] # TODO: determine how to get rid of the part before the "try" - [ 205s] # currently removing it has a performance regression for [ 205s] # unitspherical because of the try-related overhead. [ 205s] # Also frames have no way to indicate what the "distance" is [ 205s] if repr_kwargs.get("distance", True) is None: [ 205s] del repr_kwargs["distance"] [ 205s] [ 205s] if ( [ 205s] issubclass(representation_cls, r.SphericalRepresentation) [ 205s] and "distance" not in repr_kwargs [ 205s] ): [ 205s] representation_cls = representation_cls._unit_representation [ 205s] [ 205s] try: [ 205s] > representation_data = representation_cls(copy=copy, **repr_kwargs) [ 205s] [ 205s] /usr/lib64/python3.9/site-packages/astropy/coordinates/baseframe.py:456: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] self = <[AttributeError("'UnitSphericalRepresentation' object has no attribute '_lon'") raised in repr()] UnitSphericalRepresentation object at 0x7fcaec233e80> [ 205s] lon = {'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value': 266.4051, 'wrap_angle': {'unit': 'deg', 'value': 360.0}}}, 'type': 'UnitSphericalRepresentation'} [ 205s] lat = None, differentials = None, copy = True [ 205s] [ 205s] def __init__(self, lon, lat=None, differentials=None, copy=True): [ 205s] > super().__init__(lon, lat, differentials=differentials, copy=copy) [ 205s] [ 205s] /usr/lib64/python3.9/site-packages/astropy/coordinates/representation/spherical.py:53: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] self = <[AttributeError("'UnitSphericalRepresentation' object has no attribute '_lon'") raised in repr()] UnitSphericalRepresentation object at 0x7fcaec233e80> [ 205s] differentials = None [ 205s] args = ({'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value': 266.4051, 'wrap_angle': {'unit': 'deg', 'value': 360.0}}}, 'type': 'UnitSphericalRepresentation'}, None) [ 205s] kwargs = {'copy': True} [ 205s] [ 205s] def __init__(self, *args, differentials=None, **kwargs): [ 205s] # Handle any differentials passed in. [ 205s] > super().__init__(*args, **kwargs) [ 205s] [ 205s] /usr/lib64/python3.9/site-packages/astropy/coordinates/representation/base.py:672: [ 205s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [ 205s] [ 205s] self = <[AttributeError("'UnitSphericalRepresentation' object has no attribute '_lon'") raised in repr()] UnitSphericalRepresentation object at 0x7fcaec233e80> [ 205s] args = [], kwargs = {'copy': True}, components = ('lon', 'lat') [ 205s] attrs = [{'components': {'lat': {'unit': 'deg', 'value': -28.936175}, 'lon': {'unit': 'deg', 'value': 266.4051, 'wrap_angle': {'unit': 'deg', 'value': 360.0}}}, 'type': 'UnitSphericalRepresentation'}] [ 205s] component = 'lat', attr = None [ 205s] [ 205s] def __init__(self, *args, **kwargs): [ 205s] # make argument a list, so we can pop them off. [ 205s] args = list(args) [ 205s] components = self.components [ 205s] if ( [ 205s] args [ 205s] and isinstance(args[0], self.__class__) [ 205s] and all(arg is None for arg in args[1:]) [ 205s] ): [ 205s] rep_or_diff = args[0] [ 205s] copy = kwargs.pop("copy", True) [ 205s] attrs = [getattr(rep_or_diff, component) for component in components] [ 205s] if "info" in rep_or_diff.__dict__: [ 205s] self.info = rep_or_diff.info [ 205s] [ 205s] if kwargs: [ 205s] raise TypeError( [ 205s] "unexpected keyword arguments for case " [ 205s] f"where class instance is passed in: {kwargs}" [ 205s] ) [ 205s] [ 205s] else: [ 205s] attrs = [] [ 205s] for component in components: [ 205s] try: [ 205s] attr = args.pop(0) if args else kwargs.pop(component) [ 205s] except KeyError: [ 205s] raise TypeError( [ 205s] "__init__() missing 1 required positional " [ 205s] f"argument: {component!r}" [ 205s] ) from None [ 205s] [ 205s] if attr is None: [ 205s] > raise TypeError( [ 205s] "__init__() missing 1 required positional argument:" [ 205s] f" {component!r} (or first argument should be an instance of" [ 205s] f" {self.__class__.__name__})." [ 205s] ) [ 205s] E TypeError: ICRS() missing 1 required positional argument: 'dec' (or first argument should be an instance of UnitSphericalRepresentation). [ 205s] [ 205s] /usr/lib64/python3.9/site-packages/astropy/coordinates/representation/base.py:191: TypeError [ 205s] ----------------------------- Captured stdout call ----------------------------- [ 205s] Example: A Galactocentric frame without data [ 205s] From file: /home/abuild/rpmbuild/BUILD/asdf_coordinates_schemas-0.3.0/resources/schemas/frames/galactocentric-1.1.0.yaml [ 205s] =========================== short test summary info ============================ [ 205s] FAILED resources/schemas/frames/galactocentric-1.1.0.yaml::test_example_0 - T... [ 205s] ================== 1 failed, 123 passed in 171.64s (0:02:51) =================== ```
braingram commented 8 months ago

Thanks for opening the issue.

Unfortunately that is expected and unavoidable (without requiring a development version of asdf-astropy which pypi will not accept). A new version of asdf-astropy will soon be released to fix the failing test (which is also seen on main).

In brief, the schemas provided by this package are supported by asdf-astropy. Adding a new schema (like galactocentric-1.1.0) requires informing asdf-astropy of the new schema. However it can't be aware of the schema until this package is released (which is leading to the failure here). This means for some schema changes (like the one here) some tests will fail.

bnavigator commented 8 months ago

Thanks for the clarification. We have a policy to run unit tests for python packages in openSUSE and of course we test with released versions. I will skip this particular one.

braingram commented 8 months ago

Thanks. We certainly should fix this issue with the tests failing for these schema versions. I'll give it some more thought with an eye towards making it easier for you wonderful packaging folks :)

braingram commented 8 months ago

A new version of asdf-astropy (0.6.0) was released: https://pypi.org/project/asdf-astropy/0.6.0/ that adds support for the examples in the schemas to allow the tests to pass.

I'm going to unpin this issue and leave it open. Let me know if you run into any more issues and if all goes well feel free to close the issue.

bnavigator commented 7 months ago

All is well