After one too many times having accidentally typed import datetime
when what was really needed was from datetime import datetime
, I wondered whether it was possible to hack around and make the former do the latter.
That is, to recreate this behaviour (in a freshly opened interpreter session):
$ python -ic ''
>>> import datetime
>>> datetime(2016, 5, 27)
datetime.datetime(2016, 5, 27, 0, 0)
Came pretty close to faking a "callable module" below:
>>> import dt
>>> dt(2016, 5, 27)
datetime.datetime(2016, 5, 27, 0, 0)
Which was implemented like this:
# dt.py
import sys
import datetime
class CallableModule(object):
def __init__(self, thing):
self.thing = thing
def __call__(self, *args, **kwargs):
return self.thing.__call__(*args, **kwargs)
sys.modules['dt'] = CallableModule(datetime.datetime)
However it doesn't work if I try to use the filename datetime.py
for the module, and I could not yet find any hack to get at the built-in datetime module when my own file was also called datetime.py
.
How can we temporarily unhide a built-in or site-packages module, from within the shadowing module itself? Is there any indirect way to get at the core datetime
under this situation (perhaps similar to how we can still access sys.__stdout__
even when sys.stdout
has been redirected)?
Disclaimer: In no way suggesting that this is a sane idea - just interested if it's possible.
Here we go:
datetime.py
:
import sys
import imp
import os
path = [p for p in sys.path if p != os.path.dirname(__file__)]
f, pathname, desc = imp.find_module('datetime', path)
std_datetime = imp.load_module('datetime', f, pathname, desc)
# if this^ is renamed to datetime, everything breaks!
f.close()
class CallableModule(object):
def __init__(self, module, fn):
self.module = module
self.fn = fn
def __call__(self, *args, **kwargs):
return self.fn(*args, **kwargs)
def __getattr__(self, item):
try:
return getattr(self.fn, item)
except AttributeError:
return getattr(self.module, item)
sys.modules['datetime'] = CallableModule(std_datetime, std_datetime.datetime)
test.py
(lives next to datetime.py
):
import datetime
print(datetime(1, 2, 3))
print(datetime.timedelta(days=1))
print(datetime.now())
Run test.py
, output:
0001-02-03 00:00:00
1 day, 0:00:00
2016-05-27 23:16:30.954270
It also works with from datetime import datetime, timedelta
etc.
This is especially hacky and fragile and will depend on your distribution. For example, apparently it doesn't work with IPython. You must import datetime.py
before the standard library module.
To understand just how weird things get with this, if the variable std_datetime
(the datetime module object) is renamed to datetime
, then datetime.datetime
is no longer the class, but rather datetime is datetime.datetime is datetime.datetime.datetime ...
. If someone can explain why this happens, I'd love to hear it.
(note that the first comment below is from before I got to the final version)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With