Skip to content

Instantly share code, notes, and snippets.

@txomon
Last active November 16, 2018 18:56
Show Gist options
  • Save txomon/8b7b82b6e105d237a095ed27f2726465 to your computer and use it in GitHub Desktop.
Save txomon/8b7b82b6e105d237a095ed27f2726465 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
import string
from weakref import WeakKeyDictionary
class SafeDict(dict):
def __missing__(self, key):
return '{' + key + '}'
class GBQResource(str):
def __init__(self, value):
self.all_values = WeakKeyDictionary()
super(GBQResource, self).__init__(value)
self.default_value = value
self.project = self._dataset = self._table = None
values = value.split('.')
if len(values) not in (1, 2, 3):
raise ValueError('Resource needs to be {project}[.{dataset}[.{table}]]')
if len(values) == 1:
self.project, = values
elif len(values) == 2:
self.project, self._dataset = values
elif len(values) == 3:
self.project, self._dataset, self._table = values
def __get__(self, instance, owner):
if not self.all_values.get(instance):
obj = self.all_values[instance] = type(self)(self.default_value)
return obj
return self.all_values[instance]
def __set__(self, instance, value):
self.all_values[instance] = type(self)(value)
@property
def dataset(self):
if self._dataset:
return self._dataset
raise ValueError('Resource is only project, can not provide dataset')
@property
def table(self):
if self._table:
return self._table
raise ValueError('Resource is either project or dataset, can not provide table')
class CLASS(object):
def __new__(cls, *args, **kwargs):
fmt = string.Formatter()
obj = super(CLASS, cls).__new__(cls)
for key, value in kwargs.items():
if hasattr(obj, key):
setattr(obj, key, value)
obj_attrs = {a: getattr(obj, a) for a in dir(obj) if a == a.upper()}
for i in range(5): # Four indirection levels
for attribute, value in obj_attrs.items():
if not isinstance(value, (str, unicode)):
continue
obj_attrs[attribute] = fmt.vformat(value, (), SafeDict(obj_attrs))
for attribute, value in obj_attrs.items():
if not isinstance(value, (str, unicode)):
continue
assert '{' not in value, 'Attribute ' + attribute + ' failed with ' + value
assert '}' not in value, 'Attribute ' + attribute + ' failed with ' + value
setattr(obj, attribute, value)
return obj
FIELD = GBQResource('m.n.o')
a = CLASS(FIELD='z.x.y')
b = CLASS(FIELD='d.e.f')
c = CLASS()
print(a, a.FIELD, a.FIELD.table)
print(b, b.FIELD, b.FIELD.table)
print(c, c.FIELD, c.FIELD.table)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment