Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Python metaclass experimentation (python 2.7) applied on creating paths
""" experiment on whether we can make a more intuitive interface
for paths by (ab)using metaclasses :D
result :
s = DotPath.myfolder
versus:
s = os.path.join('home','user','myfolder')"""
import os
import codecs
def userpath(function, *args):
"""Decorator that can be used to wrap functions returning paths
so that they return user paths instead. Calling this function
on a path already user-expanded should have no effect."""
def expand(*args):
joined_path = function(*args)
return os.path.expanduser(joined_path)
return expand
class DynamicStatic(type):
"""Classes which inherit this type get their constructor called with
any attempted attribute access on the class.
(on the class not the class instance - ex - Path.dog versus Path().dog)"""
def __getattr__(self, name):
""" return instance of class and pass name to its constructor"""
# uses DynamicStatic __call__ procedure
# since it is called with an instance of DynamicStatic
return self(name)
def __repr__(self):
""" do whatever the sub __repr__ func will do """
# TODO check these exist..
return repr(self())
def __str__(self):
""" do whatever the sub __str__ func will do """
# TODO check these exist..
return str(self())
def __call__(self, *args):
# We can't call our constructor since we have overrided call
# so we need to use __new__ and __init__
cl = self.__new__(self)
cl.__init__(*args)
return cl
class DotPath(object):
__metaclass__ = DynamicStatic
READ = 'r'
WRITE = 'w'
PERMISSIONS = (READ, WRITE)
"""Docstring for DotPath. """
def __init__(self, *items):
self.file_permission = DotPath.READ
self.path_tokens = ['~']
self.path_tokens.extend(items)
def __getattr__(self, name):
""" Whenever we access something that doesnt exist
assume add it to the path we are creating.. """
self.path_tokens.append(name)
return self
def __call__(self, permission, encoding='utf-8'):
if permission in DotPath.PERMISSIONS:
self.file_permission = permission
self.encoding = encoding
return self
else:
raise TypeError('permission must be in %s' % DotPath.PERMISSIONS)
def __enter__(self):
self.opened_file = codecs.open(str(self),
self.file_permission,
encoding=self.encoding)
return self.opened_file
def __exit__(self, exc_type, exc_val, exc_tb):
self.opened_file.close()
@userpath
def __repr__(self):
"""get string representation of the dotpath on the callers os"""
return os.path.join(*self.path_tokens)
@userpath
def __str__(self):
"""get string representation of the dotpath on the callers os"""
return os.path.join(*self.path_tokens)
if __name__ == "__main__":
# ok, so suppose we want to create a path to the file shrubbery
path = DotPath.Documents.shrubbery
# path is an instance of DotPath with a list of the path_tokens
# which grew each time . (getattr) was used on DotPath
print(path)
# well, since we got this far, why not make the class usable
# using the with - as statement?
# problem is - we need a way to pass arguments!
# but we can use override the __call__ function for that ^_^
with path('w') as out:
msg = 'it works!'
print('writing message "%s" to %s\n' % (msg, path))
out.write(msg)
with path('r') as out:
print('reading from %s' % path)
print(out.read())
/home/grv/Documents/shrubbery
writing message "it works!" to /home/grv/Documents/shrubbery
reading from /home/grv/Documents/shrubbery
it works!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment