pyyaml icon indicating copy to clipboard operation
pyyaml copied to clipboard

deregister representer

Open casperdcl opened this issue 5 years ago • 2 comments

Feature request for ability to undo yaml.add_representer().

For example, to temporarily change the representation (so as to not affect other modules using yaml):

import yaml

def represent_list(self, data, flow_style=True):
    """Compact list using flow_style=True"""
    return self.represent_sequence('tag:yaml.org,2002:seq', data, flow_style=flow_style)


print(yaml.dump({"foo": [1, 2]}))
yaml_representers_default = yaml.representer.Representer.yaml_representers.copy()
yaml.add_representer(list, represent_list)
print(yaml.dump({"foo": [1, 2]}))

# now how to go back to previous representation?

TL;DR: it would be a good idea to implement yaml.get_representers() and yaml.set_representers(). That would make implementing a context manager very easy.

A clunky and imperfect way to undo for this particular case, assuming the previous representer was the default one:

yaml.add_representer(list, yaml_representers_default[list])
print(yaml.dump({"foo": [1, 2]}))

Ideally should be able to do something like:

print(yaml.dump({"foo": [1, 2]}))
with restore_representers(list):
    yaml.add_representer(list, represent_list)
    print(yaml.dump({"foo": [1, 2]}))
print(yaml.dump({"foo": [1, 2]})) 

which is possible with something like:

from contextlib import contextmanager
@contextmanager
def restore_representers(*data_types):
    # save old reprs
    #yaml_representers = yaml.get_representers()
    yaml_representers = yaml.representer.Representer.yaml_representers.copy()

    yield

	# restore old reprs
    #yaml.set_representers(yaml_representers)
    for data_type in data_types:
        yaml.add_representer(data_type, yaml_representers[data_type])

specifying data_types should be made unnecessary if the commented-out functions are implemented.

casperdcl avatar Sep 08 '20 14:09 casperdcl

Making these kinds of tactical modifications easier is definitely worth discussion. It's a pretty significant amount of hassle to modify the built-in Loaders and Dumpers in a way that doesn't affect other usages of pyyaml in the same process (ie, you have to re-declare the entire type hierarchy down to the level you're modifying). That said, IMO, it shouldn't be done by mutating and restoring global state on the existing types- just because it's being done with a context manager doesn't mean you can't end up in a situation where code outside your direct control ends up using one of the modified types (eg, if it gets called by 3rd party code in your callstack while the tweak context manager is active).

If anything, maybe it's worth exploring either:

  • supporting passing a tweaked instance (not a type) of a Loader/Dumper to yaml.load|dump
  • providing a type cloning mechanism to clone and mutate existing Loader/Dumper type hierarchies without requiring that the type declaration be repeated

Either of these would prevent the situation where a modified type breaks existing code that wasn't aware of it.

nitzmahone avatar Sep 08 '20 18:09 nitzmahone

Is there any easy way to apply this ? I can't seem to undo add_representer or add_constructor flows.

AvnerCohen avatar Sep 23 '24 10:09 AvnerCohen