Is it possible to convert a namespace object from mutable to immutable?
I am getting a namespace object from command line arguments. And I don't want to change it. Can I do this or do you have any ideas?
# -*- coding: utf-8 -*-
import argparse
def parse_args():
parser = argparse.ArgumentParser(description='This script is ...')
parser.add_argument('--confdir', type=str, required=True)
parser.add_argument('--outdir', type=str, required=True)
return parser.parse_args()
if __name__ == '__main__':
mutable_namespace = parse_args()
# I want to prevent overwrite like that.
mutable_namespace.confdir = "xxx"
source to share
I suggested my own namespace class at first, but I like this idea of ββcopying args
to NamedTuple better.
Namedtuple
Another option is to copy the values ββfrom args
into an immutable object / class. A named tuple can do the job nicely.
Create namespace
In [1157]: dest=['x','y']
In [1158]: args=argparse.Namespace()
In [1159]: for name in dest:
......: setattr(args, name, 23)
......:
In [1160]: args
Out[1160]: Namespace(x=23, y=23)
now define namedtuple
In [1161]: from collections import namedtuple
In [1163]: Foo = namedtuple('Foo',dest)
You can also get the names of the tuples from the namespace itself (after parsing)
Foo = namedtuple('Foo',vars(args).keys())
create such a tuple with values ββfrom args
:
In [1165]: foo=Foo(**vars(args))
In [1166]: foo
Out[1166]: Foo(x=23, y=23)
In [1167]: foo.x
Out[1167]: 23
and it is immutable:
In [1168]: foo.x=34
...
AttributeError: can't set attribute
Such a named element cannot be used as a namespace because it setattr(foo,'x',34)
generates the same error.
The clean way to do it all is to wrap the whole thing in a function:
def do_parse():
parser = ....
Foo = namedtuple(...)
args = parser.parse_args()
foo = Foo(**vars(args))
return foo
The calling code never sees mutable args
, just immutable foo
.
Custom namespace class
To build the answer Ingaz
, argparse
you can use your own class Namespace
.
https://docs.python.org/3/library/argparse.html#the-namespace-object
class MyNamespace(argparse.Namespace):
pass
<customize one or more methods>
anamespace = MyNamespace()
args = parser.parse_args(namespace=anamespace)
Now args
and anamespace
refer to the same object MyNamespace
. So far, getattr(anamespace, adest)
and setattr(anamespace, adest, avalue)
work, argparse
you can use the namespace object.
Now you can allow setattr(anamespace, 'string', 'value')
but deny anamespace.string = value
? I think you can, but it will require a good understanding of how the last expression works. It might just require customization .__setattr__
, but I haven't learned this aspect of Python in a while.
By design, this is possible and even acceptable for "monkey patches" in the namespace argparse
- with a custom class like this.
source to share
You can override __setattr__
mutable_namespace in your space:
class NotMutableException(Exception):pass
class SomeObject(object):
def init(self):
self.x = 10
self.y = 20
some_obj = SomeObject()
some_obj.z = 30
def not_setattr(self, name, value):
raise NotMutableException
type(some_obj).__setattr__ = not_setattr
some_obj.a = 1000
source to share