Created
October 25, 2018 09:15
-
-
Save theelous3/03dda1f7074d95a81c50ebb5cb9aaa10 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from threading import BoundedSemaphore | |
from types import GeneratorType | |
from collections.abc import MappingView, Sequence | |
from functools import partial | |
_ALLOWED_SEQUENCE_TYPES = ( | |
Sequence, | |
MappingView, | |
GeneratorType | |
) | |
class Locker: | |
def __init__(self, obj, blocking=True, read='read', write='write'): | |
self.obj = obj | |
self.blocking = blocking | |
self._write_sema = BoundedSemaphore(value=1) | |
self._writeable = True | |
#handle lists of attribes to read and write protect | |
if not isinstance(read, str): | |
self._validate_containery_types(read) | |
read_dict = { | |
sub_read: partial(self._read, getattr(obj, sub_read)) | |
for sub_read in read | |
} | |
else: | |
read_dict = read_dict = {read: partial(self._read, getattr(obj, read))} | |
if not isinstance(write, str): | |
self._validate_containery_types(write) | |
write_dict = { | |
sub_write: partial(self._write, getattr(obj, sub_write)) | |
for sub_write in write | |
} | |
else: | |
write_dict = {write: partial(self._write, getattr(obj, write))} | |
self.__dict__.update(read_dict) | |
self.__dict__.update(write_dict) | |
def __getattr__(self, attr): | |
try: | |
return self.__dict__[attr] | |
except KeyError: | |
raise AttributeError("Locker wrapped '{}' object has no" | |
" attribute '{}'".format(type(self.obj), attr) | |
) | |
def __repr__(self): | |
return "Locker {} ({} {})".format( | |
hex(id(self)), | |
type(self.obj), | |
hex(id(self.obj))) | |
def _write(self, wrapped_method, *args, **kwargs): | |
if self._write_sema._value < 1: | |
if not self.blocking: | |
raise RuntimeError( | |
"Cannot write to non-blocking protected {} when it" | |
"is in use.".format(type(self.obj)) | |
) | |
with self._write_sema: | |
return wrapped_method(*args, **kwargs) | |
def _read(self, wrapped_method, *args, **kwargs): | |
if self._write_sema._value < 1: | |
raise RuntimeError( | |
"Cannot read to non-blocking protected {} when it" | |
"is in write.".format(type(self.obj)) | |
) | |
return wrapped_method(*args, **kwargs) | |
@staticmethod | |
def _validate_containery_types(x): | |
""" | |
Validates that the thing passed is some sort of sane | |
container of data. Why is there no collections.abc of | |
"thing you can put things in that isn't a str"!? | |
""" | |
if not isinstance(x, _ALLOWED_SEQUENCE_TYPES): | |
raise TypeError("Cannot pass {} as container of method names.".format(type(x))) | |
pstr = Locker(StringIO(), read=['getvalue', 'read']) | |
pstr.write('lol') | |
pstr.read() | |
pstr.getvalue() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment