Closed erikw closed 2 years ago
An important thing to know is that loads takes an argument top that determines both the expected and the allowed form of the top-level object. This was done because people often just assume that the top level is a dictionary when they write their code, and their code would crash if they got an empty file or something other than a dictionary. To mitigate that, we could have restricted the format to only allow dictionaries at the top level, but we did not want to give up that flexibility. So we opted for a somewhat more confusing approach where the developer explicitly specifies the type of the top-level object.
Also, None is not officially supported as a data type in NestedText documents. My implementation of dumps converts it to an empty string by default, but if you pass default="strict"
as an argument it raises an error. However, there is one exception. None is a valid type for the top-level object, and it signifies an empty NT document. Thus dumps(None)
produces ""
. When reading an empty document None is returned if top is any. If top is not any the None is converted to an empty object of the expected type.
When dumping, I overlooked None passed as a key. Currently it is converted to the string "None", but as you say, that seems inconsistent and so I will have to rethink that behavior. I will probably convert it to a empty string to be consistent, as you suggest.
You are seeing the expected behavior. None when dumped creates an empty NT document, which loads as an empty dictionary because by default top is dict.
You are seeing the expected behavior. In this case the error from loads results not from the None, but because the top-level object in the NT document is a list. By default, loads expects a dictionary.
You are seeing the expected behavior. By default None is converted to an empty string when passed as a value.
As I mentioned above, this one surprised me. I think the None should probably be represented by an empty string when passed as a key.
Here is some Python code that tests these various case. Perhaps it will be helpful to you:
import nestedtext as nt
from inform import error
cases = [
# given, top, dumped, loaded
( None, dict, '', {} ),
( None, list, '', [] ),
( None, str, '', "" ),
( None, any, '', None ),
( [None], list, '-', [''] ),
( [None], any, '-', [''] ),
( {'key':None}, dict, 'key:', {'key':''} ),
( {None:'val'}, dict, 'None: val', {'None':'val'} ), # ← wrong?
]
for given, top, dump_expected, load_expected in cases:
dumped = nt.dumps(given)
if dumped != dump_expected:
error(f"dumps({given}) gives {dumped}, expected {dump_expected}")
loaded = nt.loads(dumped, top)
if loaded != load_expected:
error(f"loads({given}, {top}) gives {loaded}, expected {load_expected}")
# some special cases
try:
nt.dumps([None], default='strict')
error("expected exception on dumps([None]).")
except nt.NestedTextError:
pass
try:
nt.loads('-')
error("expected exception on loads('-').")
except nt.NestedTextError:
pass
A Ruby implementation, that is great news!
Thank you for the explanation!
I'll leave the issue open regarding None
as a key.
I have decided to convert None in keys to the empty string to make keys consistent with values.. I have implemented this change in my local version and it my local tests. I am not yet committing those changes to github until I get comfortable with some new features I have implemented, but I wanted to let you know the direction I was going so you can do the same in your version.
Thank you for the heads-up. I will do the same in nestedtext-ruby :).
Okay, I finally updated the GitHub version with this change.
Hello,
I'm on a good way to make a Ruby implementation of NestedText (erikw/nestedtext-ruby, all official decode tests are passing!) and I'm currently writing unit tests for various edge-case inputs. Using the Python implementation, I'm unsure what the expectation is on encoding python's
None
(and ruby'snil
in my case). I see thatNone
is treated in different ways.Using this base-program and just changing the
obj = ...
line in the different examples:Just
None
gives the output:
▶️
None
encodes to empty string, but is decoded back as empty inline dictNone
in listgives the output:
▶️
None
encodes to empty string, but can't be decoded back to PythonNone
as dict valuegives the output:
▶️
None
encodes to empty string, but is decoded back to empty string and notNone
None
as dict keygives the output:
▶️
None
encodes to the string "None", and decodes back to the string "None"Thus,
None
is treated different in all cases above. The encoding from Python is of course not a part of the specification for the NT data format, but it would still be nice to know the rules forNone
, or thatNone
is always treated the same in all cases :).Is the output above the expected? I suspect that in the case with
None
I'm happy to hear your thoughts!