Getting Started
Installation
From PyPI:
pip install RecursiveNamespaceV2
# or with uv
uv pip install RecursiveNamespaceV2
From Source (using uv):
git clone https://github.com/pasxd245/RecursiveNamespaceV2.git
cd RecursiveNamespaceV2
uv venv
uv pip install -e ".[test]"
Basic Usage
Creating a Recursive Namespace
from recursivenamespace import RNS
# From a dictionary
data = {'name': 'John', 'age': 30}
rn = RNS(data)
# From keyword arguments
rn = RNS(name='John', age=30)
# Access values
print(rn.name) # John
print(rn['age']) # 30
Nested Structures
data = {
'user': {
'profile': {
'name': 'John',
'email': 'john@example.com'
}
}
}
rn = RNS(data)
print(rn.user.profile.name) # John
Converting Back to Dictionary
Public methods live under the obj._ proxy. Calling them directly on
the instance (e.g. rn.to_dict()) still works in Phase 1 of the
migration but emits a DeprecationWarning — prefer the proxy form:
rn = RNS({'a': {'b': {'c': 1}}})
# Regular dict
d = rn._.to_dict()
# Flattened dict
flat_d = rn._.to_dict(flatten_sep='_')
# {'a_b_c': 1}
See Method Proxy (obj._) for the full migration story.
Key Normalization
By default, hyphens, dots, and whitespace are converted to underscores so the value is reachable as both an attribute and an item:
rn = RNS({'some-key': 'value'})
print(rn.some_key) # value
print(rn['some-key']) # value (both work)
To preserve original keys (no normalization):
rn = RNS(data, use_raw_key=True)
Reserved and Protected Keys
Two tiers of names are reserved on the class:
Hard-protected — every single-underscore attribute on
RNS(the_method proxy plus internal helpers like_re_,_process_,_chain_*_,_logger_). Using one of these as a data key raisesKeyError.Soft-protected (deprecated public methods) — names like
to_dict,val_set,val_get,update,keys,values,items,copy,deepcopy,pop,as_schema,to_json/from_json,to_toml/from_toml, etc. Storing a data field with one of these names is allowed but emits aFutureWarning:obj[name]/obj.namewill return the data, and the method remains reachable viaobj._.<name>(...).FutureWarningis used (instead ofDeprecationWarning) so the collision is visible under Python’s default warning filter — you do not need-W defaultto see a shadow event the first time it happens.
Classmethod Factories
Deserializers are classmethods, not instance methods — call them on
RNS directly, not through the _ proxy:
rn = RNS.from_json('{"a": 1, "b": {"c": 2}}')
rn = RNS.load_json('config.json')
rn = RNS.from_toml('a = 1\nb.c = 2')
rn = RNS.load_toml('config.toml')
Round-trip with the instance serializers:
rn = RNS(a=1, b={'c': 2})
text = rn._.to_json()
rn._.save_json('out.json')
same = RNS.from_json(text)