wwkimball / yamlpath

YAML/JSON/EYAML/Compatible get/set/merge/validate/scan/convert/diff processors using powerful, intuitive, command-line friendly syntax.
https://github.com/wwkimball/yamlpath/wiki
ISC License
120 stars 23 forks source link

Wrappers around values added via set_value #180

Closed tsinggggg closed 2 years ago

tsinggggg commented 2 years ago

When you add any value to a Processor, the added value will get wrapped with a data type like PlainScalarString.

Is there a way around this and keep the original data type?

I was kind of hacking it like

Nodes.make_new_node = lambda source_node, value, value_format, **kwargs: value

The current implementation has 2 disadvantages this way:

wwkimball commented 2 years ago

Please open a proper bug report. I can't see where the failure is or test your issue with this little information.

Having said that, I'm not sure your expectations are quite right. The wrapper types you speak of are the native ruamel.yaml types and are required by ruamel.yaml to properly round-trip the data<->stream. What issue are you really having?

tsinggggg commented 2 years ago

Thanks for the response. I am not sure if this is a bug, or I am abusing the package.

Under this setup, I was trying to create a Processor, add something to the underlying data, then serialize it.

OS: MacOS 12.3.1 (21E258)
Python 3.9.12
yamlpath           3.6.4
ruamel.yaml        0.17.17

With the script

from types import SimpleNamespace

from ruamel.yaml import YAML
from yamlpath.wrappers import ConsolePrinter
from yamlpath import Processor
from yamlpath.enums.yamlvalueformats import YAMLValueFormats

if __name__ == '__main__':
    log = ConsolePrinter(
        SimpleNamespace(quiet=True, verbose=False, debug=False)
    )
    config = {"key1": [1, 2, 3],
              "key2": {
                  "key3": "value1",
                  "key4": "value2",
              }
              }
    processor = Processor(log, config)
    processor.set_value("x", "asdf", value_format=YAMLValueFormats.DEFAULT)

    with open("test.yml", "w", encoding="utf-8") as cfgyaml:
        yaml = YAML(typ="safe")
        yaml.default_flow_style = False
        yaml.dump(config, cfgyaml)

Do I mistakenly expect this to work? currently it throws an error ruamel.yaml.representer.RepresenterError: cannot represent an object

wwkimball commented 2 years ago

You're forming a bit of a mixed bag by using yamlpath classes against bare ruamel.yaml classes. Let yamlpath handle the boilerplate ruamel.yaml setup for you and this cleans right up:

from types import SimpleNamespace

from yamlpath.wrappers import ConsolePrinter
from yamlpath import Processor
from yamlpath.enums.yamlvalueformats import YAMLValueFormats
from yamlpath.common import Parsers

if __name__ == '__main__':
    log = ConsolePrinter(
        SimpleNamespace(quiet=True, verbose=False, debug=False)
    )
    config = {"key1": [1, 2, 3],
              "key2": {
                  "key3": "value1",
                  "key4": "value2",
              }
              }
    processor = Processor(log, config)
    processor.set_value("x", "asdf", value_format=YAMLValueFormats.DEFAULT)

    with open("test.yml", "w", encoding="utf-8") as cfgyaml:
        yaml = Parsers.get_yaml_editor()
        yaml.dump(config, cfgyaml)
$ ls -1
180.py

$ python ./180.py 

$ ls -1
180.py
test.yml

$ cat ./test.yml 
---
key1:
  - 1
  - 2
  - 3
key2:
  key3: value1
  key4: value2
x: asdf
wwkimball commented 2 years ago

I'm closing this out as a non-issue. There is abundant documentation about using the yamlpath helpers:

tsinggggg commented 2 years ago

Thanks for the suggestion!

A follow up question, under the same setting, list(processor.get_nodes("x"))[0].node returns a PlainScalarString, is this the intended way to do it? or is there a way I can query the path "x" and returns a primitive string?

wwkimball commented 2 years ago

https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/scalarstring.py is a just a primitive str with some dressings necessary for ruamel.yaml to properly round-trip YAML data while preserving all formatting, comments, and so on. Operations you'd normally expect to run with or against a primitive str will work against the ruamel.yaml string extensions. If you absolutely had to strip off the extra dressings anyway, use str():

>>> import ruamel.yaml

>>> ys = ruamel.yaml.scalarstring.PlainScalarString("abc")

>>> ys
'abc'

>>> type(ys)
<class 'ruamel.yaml.scalarstring.PlainScalarString'>

>>> isinstance(ys, str)
True

>>> type(str(ys))
<class 'str'>