Created
November 23, 2018 00:22
-
-
Save theelous3/3991905bbe43414be58f08948b62d473 to your computer and use it in GitHub Desktop.
This file contains 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))) | |
from io import StringIO | |
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