def _copy_logic( cls, cassette: Cassette, pathname: str, keys: list, ret_store_cls: Any, return_value: Any, ) -> Any: """ Internal function. Copy files to or back from persisten storage It will create tar archive with tar_compression and stores it to Persistent Storage """ FILENAME = "filename" TARGET_PATH = "target_path" RETURNED = "return_value" serialization = ret_store_cls(store_keys=["not_important"], cassette=cassette) logger.debug(f"Copy files {pathname} -> {keys}") logger.debug(f"Persistent Storage mode: {cassette.mode}") original_cwd = os.getcwd() if cassette.do_store(keys=cls.basic_ps_keys + keys): try: artifact_name = os.path.basename(pathname) artifact_path = os.path.dirname(pathname) os.chdir(artifact_path) with BytesIO() as fileobj: with tarfile.open(mode=f"x:{cls.tar_compression}", fileobj=fileobj) as tar_store: tar_store.add(name=artifact_name) metadata = {cassette.data_miner.LATENCY_KEY: 0} file_name = cls.store_file_content( cassette=cassette, file_name=os.path.basename(pathname), content=fileobj.getvalue(), ) serialized = serialization.to_serializable(return_value) output = { FILENAME: file_name, RETURNED: serialized, TARGET_PATH: pathname, } cassette.store( keys=cls.basic_ps_keys + keys, values=output, metadata=metadata, ) finally: os.chdir(original_cwd) else: output = cassette[cls.basic_ps_keys + keys] pathname = pathname or output[RETURNED] content = cls.read_file_content(cassette=cassette, file_name=output[FILENAME]) # WORKAROUND: some parts expects old dir and some new one. so copy to both to ensure. # mainly when creating complex objects. for item in [pathname, output[TARGET_PATH]]: cls.__write_file(content, item) return_value = serialization.from_serializable(output[RETURNED]) return return_value
def testWrite(self, cassette: Cassette): self.reset() response = requests.get("http://example.com") self.assertIn("This domain is for use", response.text) self.assertFalse(os.path.exists(cassette.storage_file)) cassette.dump() self.assertTrue(os.path.exists(cassette.storage_file)) os.remove(cassette.storage_file)
def test_regex_write(self, cassette: Cassette): self.assertIsNotNone(cassette) self.use_requests() self.assertIn("requests.sessions", cassette.storage_object) self.assertFalse(Path(cassette.storage_file).exists()) cassette.dump() self.assertTrue(Path(cassette.storage_file).exists()) Path(cassette.storage_file).unlink()
def test_write(self, cassette: Cassette): self.assertIsNotNone(cassette) self.assertEqual(self.count_cassette_setup_triggered, 1) self.assertEqual(self.count_cassette_teardown_triggered, 0) self.use_requests() self.assertIn("requests.sessions", cassette.storage_object) self.assertFalse(Path(cassette.storage_file).exists()) cassette.dump() self.assertTrue(Path(cassette.storage_file).exists()) Path(cassette.storage_file).unlink()
def cassette_setup_and_teardown_decorator(func): """ Decorator that triggers `cassette_setup` method to be run before the test method. """ if hasattr(func, REQURE_SETUP_APPLIED_ATTRIBUTE_NAME): return func func_cassette = (getattr(func, REQURE_CASSETTE_ATTRIBUTE_NAME) if hasattr( func, REQURE_CASSETTE_ATTRIBUTE_NAME) else None) cassette_int = func_cassette or Cassette() @functools.wraps(func) def cassette_setup_inner(self, *args, **kwargs): if hasattr(self, "cassette_setup"): self.cassette_setup(cassette=cassette_int) if ("cassette" in inspect.getfullargspec(func).annotations and inspect.getfullargspec(func).annotations["cassette"] == Cassette and "cassette" not in kwargs): kwargs["cassette"] = cassette_int return_value = func(self, *args, **kwargs) if hasattr(self, "cassette_teardown"): self.cassette_teardown(cassette=cassette_int) return return_value setattr(cassette_setup_inner, REQURE_CASSETTE_ATTRIBUTE_NAME, cassette_int) setattr(cassette_setup_inner, REQURE_SETUP_APPLIED_ATTRIBUTE_NAME, True) return cassette_setup_inner
def recording( what: str, decorate: Optional[Union[List[Callable], Callable]] = None, replace: Optional[Callable] = None, storage_file: Optional[str] = None, storage_keys_strategy=StorageKeysInspectSimple, ): """ Context manager which can be used to store calls of the function and and replay responses on the next run. :param what: str - full path of function inside module :param decorate: function decorator what will be applied to what, could be also list of decorators, to be able to apply more decorators on one function eg. store files and store output :param replace: replace original function by given one :param storage_file: path for storage file if you don't want to use default location :param storage_keys_strategy: you can change key strategy for storing data default simple one avoid to store stack information """ cassette = Cassette() cassette.storage_file = storage_file cassette.data_miner.key_stategy_cls = storage_keys_strategy # ensure that directory structure exists already os.makedirs(os.path.dirname(cassette.storage_file), exist_ok=True) # use default decorator for context manager if not given. if decorate is None and replace is None: logger.info(f"Using default decorator for {what}") decorate = Guess.decorator_plain(cassette=cassette) elif decorate is not None and replace is not None: raise ValueError( "right one from [decorate, replace] parameter has to be set.") # Store values and their replacements for modules to be able to _revert_modules changes back module_list = _parse_and_replace_sys_modules(what=what, cassette=cassette, decorate=decorate, replace=replace) try: yield cassette finally: # dump data to storage file cassette.dump() _revert_modules(module_list)
def change_storage_file(cassette: Cassette, func, args, storage_file: Optional[str] = None): """ Internal function that try to construct persistent data file based on various possibilities. :param cassette: Cassette instance to pass inside object to work with """ if storage_file: cassette.storage_file = storage_file else: if len(args): try: cassette.storage_file = get_datafile_filename(args[0]) except NameError: cassette.storage_file = get_datafile_filename(func) else: cassette.storage_file = get_datafile_filename(func) original_storage_file = cassette.storage_file return original_storage_file
def replace_decorator(func): func_cassette = (getattr( func, REQURE_CASSETTE_ATTRIBUTE_NAME) if hasattr( func, REQURE_CASSETTE_ATTRIBUTE_NAME) else None) cassette_int = cassette or func_cassette or Cassette() @functools.wraps(func) def _replaced_function(*args, **kwargs): # set storage if not set to default one, based on function name if cassette_int.storage_file is None: change_storage_file(cassette=cassette_int, func=func, args=args) cassette_int.data_miner.key_stategy_cls = storage_keys_strategy # ensure that directory structure exists already os.makedirs(os.path.dirname(cassette_int.storage_file), exist_ok=True) # Store values and their replacements for modules # to be able to _revert_modules changes back module_list = _parse_and_replace_sys_modules(what=what, cassette=cassette_int, decorate=decorate, replace=replace) try: # pass current cassette to underneath decorator and do not overwrite if set there if ("cassette" in inspect.getfullargspec(func).annotations and inspect.getfullargspec(func).annotations["cassette"] == Cassette and "cassette" not in kwargs): kwargs["cassette"] = cassette_int # execute content output = func(*args, **kwargs) except Exception as e: raise (e) finally: # dump data to storage file cassette_int.dump() _revert_modules(module_list) return output setattr(_replaced_function, REQURE_CASSETTE_ATTRIBUTE_NAME, cassette_int) return _replaced_function
def _pr_comments_test(self): token = os.environ.get("GITHUB_TOKEN") cassette = Cassette() if cassette.mode == StorageMode.write and (not token): raise EnvironmentError( f"You are in Requre write mode, please set proper GITHUB_TOKEN" f" env variables {cassette.storage_file}") # possible to check before reading values because in other case values are removed # and in write mode is does have sense at the end if cassette.mode == StorageMode.read: self.assertIn(self.id(), cassette.storage_file) self.assertIn("LGTM", str(cassette.storage_object)) self.assertTrue( cassette.storage_object["requests.sessions"]["send"]["GET"] ["https://api.github.com:443/repos/packit-service/ogr"]) service = GithubService(token=token) ogr_project = service.get_project(namespace="packit-service", repo="ogr") pr_comments = ogr_project.get_pr_comments(9) assert pr_comments assert len(pr_comments) == 2 assert pr_comments[0].body.endswith("fixed") assert pr_comments[1].body.startswith("LGTM")
def __init__(self): super().__init__() self.cassette = Cassette()
from requre.helpers.files import StoreFiles from requre.cassette import Cassette from tempfile import mkdtemp import os cassette = Cassette() cassette.storage_file = "/tmp/files.yaml" temp_dir = mkdtemp() @StoreFiles.where_arg_references(key_position_params_dict={"dir_name": 0}, cassette=cassette) def return_result(dir_name): file_name = input("enter file name: ") with open(os.path.join(dir_name, file_name), "w") as fd: fd.write("empty") return file_name print("returned file name:", return_result(temp_dir)) print("dir name (current):", temp_dir) print("files:", os.listdir(temp_dir)) cassette.dump()
import tempfile from requre.cassette import Cassette keys = ["basic", "keys", 0.1] value1 = {"cat": "tom", "mouse": "jerry"} value2 = {"cat1": "pa", "cat2": "pi"} cassette = Cassette() cassette.storage_file = tempfile.mktemp() cassette.store(keys, value1, metadata={}) cassette.store(keys, value2, metadata={}) cassette.dump() cassette.storage_object = {} cassette.load() print(cassette.storage_object) print(cassette.read(keys)) print(cassette.read(keys))
from requre.objects import ObjectStorage from requre.cassette import Cassette cassette = Cassette() @ObjectStorage.decorator_plain(cassette=cassette) def return_result(): return {"value": input("insert value: ")} cassette.storage_file = "values.yaml" value = return_result() cassette.dump() print(cassette.storage_object) print(value)
def test_write(self, cassette: Cassette): cassette.storage_file = tempfile.mktemp() out = RetTuple().ret(1) self.assertEqual(out, ("ret", 1))
) @replace_module_match(what="math.sin", decorate=Simple.decorator(item_list=[])) def testReadMultiple(self, cassette: Cassette): assert cassette if cassette.mode == StorageMode.write: self.reset() sin_output = math.sin(1.5) else: sin_output = math.sin(4) response = requests.get("http://example.com") self.assertIn("This domain is for use", response.text) self.assertAlmostEqual(0.9974949866040544, sin_output, delta=0.0005) new_cassette = Cassette() @apply_decorator_to_all_methods( replace_module_match(what="math.sin", decorate=Simple.decorator_plain(), cassette=new_cassette)) @record_requests(cassette=new_cassette) class DecoratorClassApply(unittest.TestCase): # when regeneration, comment lines with assert equals, because checks for equality does not work def setUp(self): new_cassette.storage_file = None def test0(self, cassette: Cassette): if cassette.mode == StorageMode.read: self.assertEqual(len(new_cassette.storage_object["math"]["sin"]),