def remove_recipe_files(name, version, username, channel, auth_user): """ Remove any existing conanfiles or its packages created """ # The remove files is a part of the upload process, where the revision in v1 will always # be DEFAULT_REVISION_V1 revision = DEFAULT_REVISION_V1 conan_reference = ConanFileReference(name, version, username, channel, revision) conan_service = ConanService(app.authorizer, app.server_store, auth_user) reader = codecs.getreader("utf-8") payload = json.load(reader(request.body)) files = [ os.path.normpath(filename) for filename in payload["files"] ] conan_service.remove_conanfile_files(conan_reference, files)
def setUp(self): conanfile = GenConanfile() conanfile.with_package_file("include/header.h", "whatever", link="include/header.h.lnk") self.ref = ConanFileReference("name", "version", "user", "channel") self.client = TurboTestClient() self.client.create(self.ref, conanfile) layout = self.client.cache.package_layout(self.ref) package_folder = layout.package( PackageReference(self.ref, NO_SETTINGS_PACKAGE_ID)) self.header_path = os.path.join(package_folder, "include", "header.h") self.link_path = os.path.join(package_folder, "include", "header.h.lnk")
def get_package_snapshot(name, version, username, channel, package_id, auth_user): """ Get a dictionary with all files and their each md5s """ conan_service = ConanService(app.authorizer, app.server_store, auth_user) ref = ConanFileReference(name, version, username, channel) pref = PackageReference(ref, package_id) snapshot = conan_service.get_package_snapshot(pref) snapshot_norm = { filename.replace("\\", "/"): the_md5 for filename, the_md5 in snapshot.items() } return snapshot_norm
def get_package_download_urls(name, version, username, channel, package_id, auth_user): """ Get a dict with all packages files and the download url for each one """ conan_service = ConanService(app.authorizer, app.server_store, auth_user) ref = ConanFileReference(name, version, username, channel) pref = PackageReference(ref, package_id) urls = conan_service.get_package_download_urls(pref) urls_norm = { filename.replace("\\", "/"): url for filename, url in urls.items() } return urls_norm
def test_export_ignore_case(self): conanfile = """from conans import ConanFile class ConanSymlink(ConanFile): name = "ConanSymlink" version = "3.0.0" exports_sources = ["*"] def package(self): self.copy("*NOT_TO_COPY.TXT", ignore_case=%s) """ client = TestClient() client.save({ "conanfile.py": conanfile % "False", "src/main.cpp": "cpp fake content", "CMakeLists.txt": "cmake fake content", "another_directory/not_to_copy.txt": "" }) mkdir(os.path.join(client.current_folder, "another_other_directory")) symlink_path = os.path.join(client.current_folder, "another_other_directory", "another_directory") symlinked_path = os.path.join(client.current_folder, "another_directory") self.assertFalse(os.path.exists(symlink_path)) self.assertTrue(os.path.exists(symlinked_path)) os.symlink(symlinked_path, symlink_path) client.run("create . danimtb/testing") ref = ConanFileReference("ConanSymlink", "3.0.0", "danimtb", "testing") cache_file = os.path.join( client.cache.package_layout(ref).export_sources(), "another_directory", "not_to_copy.txt") self.assertTrue(os.path.exists(cache_file)) cache_other_dir = os.path.join( client.cache.package_layout(ref).export_sources(), "another_other_directory") self.assertTrue(os.path.exists(cache_other_dir)) pref = PackageReference(ref, NO_SETTINGS_PACKAGE_ID) package_file = os.path.join( client.cache.package_layout(pref.ref).package(pref), "another_directory", "not_to_copy.txt") self.assertFalse(os.path.exists(package_file)) package_other_dir = os.path.join( client.cache.package_layout(pref.ref).package(pref), "another_other_directory") self.assertFalse(os.path.exists(package_other_dir)) client.save({"conanfile.py": conanfile % "True"}) client.run("create . danimtb/testing") self.assertTrue(os.path.exists(package_file)) self.assertTrue(os.path.exists(package_other_dir))
def load_export(self, conanfile_path, name, version, user, channel, lock_python_requires=None): """ loads the conanfile and evaluates its name, version, and enforce its existence """ conanfile = self.load_named(conanfile_path, name, version, user, channel, lock_python_requires) if not conanfile.name: raise ConanException("conanfile didn't specify name") if not conanfile.version: raise ConanException("conanfile didn't specify version") # FIXME Conan 2.0, conanfile.version should be a string, not a version object ref = ConanFileReference(conanfile.name, conanfile.version, user, channel) conanfile.display_name = str(ref) conanfile.output.scope = conanfile.display_name return conanfile
def _export_upload(self, name, version=None, deps=None): conan = TestClient(servers=self.servers, users=[("lasote", "mypass")]) dll_export = conan.default_compiler_visual_studio files = cpp_hello_conan_files(name, version, deps, static=False, dll_export=dll_export) conan_ref = ConanFileReference(name, version, "lasote", "stable") conan.save(files, clean_first=True) conan.run("export lasote/stable") conan.run("install '%s' --build missing" % str(conan_ref)) conan.run("upload %s --all" % str(conan_ref)) rmdir(conan.current_folder) shutil.rmtree(conan.paths.store, ignore_errors=True)
def _export_upload(self, name, version=None, deps=None, use_cmake=True, cmake_targets=False): files = cpp_hello_conan_files(name, version, deps, need_patch=True, use_cmake=use_cmake, cmake_targets=cmake_targets) conan_ref = ConanFileReference(name, version, "lasote", "stable") self.conan.save(files, clean_first=True) self.conan.run("export lasote/stable") self.conan.run("upload %s" % str(conan_ref))
def check_digraph_line(line): self.assertTrue(dot_regex.match(line)) node_matches = node_regex.findall(line) parent_reference = node_matches[0] deps_ref = [ConanFileReference.loads(references) for references in node_matches[1:]] if parent_reference == "conanfile.py (Hello0/0.1)": parent_ref = ConanFileReference("Hello0", None, None, None, validate=False) else: parent_ref = ConanFileReference.loads(parent_reference) check_ref(parent_ref) for dep in deps_ref: check_ref(dep) self.assertIn(dep.name, test_deps[parent_ref.name])
def search_packages(name, version, username, channel, auth_user, revision=None): v2_compatibility_mode = request.headers.get( "V2_COMPATIBILITY_MODE", "0") == "1" query = request.params.get("q", None) search_service = SearchService(app.authorizer, app.server_store, auth_user) conan_reference = ConanFileReference(name, version, username, channel, revision) info = search_service.search_packages(conan_reference, query, v2_compatibility_mode) return info
def search_recipes(cache, pattern=None, ignorecase=True): # Conan references in main storage if pattern: if isinstance(pattern, ConanFileReference): pattern = str(pattern) pattern = translate(pattern) pattern = re.compile( pattern, re.IGNORECASE) if ignorecase else re.compile(pattern) subdirs = list_folder_subdirs(basedir=cache.store, level=4) refs = [ConanFileReference(*folder.split("/")) for folder in subdirs] refs.extend(cache.editable_packages.edited_refs.keys()) if pattern: refs = [r for r in refs if _partial_match(pattern, r)] refs = sorted(refs) return refs
def load_consumer(self, conanfile_path, processed_profile, name=None, version=None, user=None, channel=None, test=None): conanfile_class = self.load_class(conanfile_path) if name and conanfile_class.name and name != conanfile_class.name: raise ConanException("Package recipe name %s!=%s" % (name, conanfile_class.name)) if version and conanfile_class.version and version != conanfile_class.version: raise ConanException("Package recipe version %s!=%s" % (version, conanfile_class.version)) conanfile_class.name = name or conanfile_class.name conanfile_class.version = version or conanfile_class.version if test: display_name = "%s (test package)" % test else: ref = ConanFileReference(conanfile_class.name, conanfile_class.version, user, channel, validate=False) if ref.name or ref.version or ref.user or ref.channel: display_name = "%s (%s)" % (os.path.basename(conanfile_path), ref) else: display_name = os.path.basename(conanfile_path) conanfile = conanfile_class(self._output, self._runner, display_name, user, channel) conanfile.in_local_cache = False try: self._initialize_conanfile(conanfile, processed_profile) # The consumer specific conanfile.develop = True processed_profile._user_options.descope_options(conanfile.name) conanfile.options.initialize_upstream( processed_profile._user_options, name=conanfile.name) processed_profile._user_options.clear_unscoped_options() return conanfile except Exception as e: # re-raise with file name raise ConanException("%s: %s" % (conanfile_path, str(e)))
def scm_sources_test(self): """ Test conan_sources.tgz is deleted in server when removing 'exports_sources' and using 'scm'""" conanfile = """from conans import ConanFile class TestConan(ConanFile): name = "test" version = "1.0" """ exports_sources = """ exports_sources = "include/*" """ servers = {"upload_repo": TestServer([("*/*@*/*", "*")], [("*/*@*/*", "*")], users={"lasote": "mypass"})} client = TestClient(servers=servers, users={"upload_repo": [("lasote", "mypass")]}) client.save({"conanfile.py": conanfile + exports_sources, "include/file": "content"}) client.run("create . danimtb/testing") client.run("upload test/1.0@danimtb/testing -r upload_repo") self.assertIn("Uploading conan_sources.tgz", client.out) ref = ConanFileReference("test", "1.0", "danimtb", "testing") rev = servers["upload_repo"].server_store.get_last_revision(ref).revision ref = ref.copy_with_rev(rev) export_sources_path = os.path.join(servers["upload_repo"].server_store.export(ref), "conan_sources.tgz") self.assertTrue(os.path.exists(export_sources_path)) scm = """ scm = {"type": "git", "url": "auto", "revision": "auto"} """ client.save({"conanfile.py": conanfile + scm}) with chdir(client.current_folder): client.runner("git init") client.runner('git config user.email "*****@*****.**"') client.runner('git config user.name "Your Name"') client.runner("git remote add origin https://github.com/fake/fake.git") client.runner("git add .") client.runner("git commit -m \"initial commit\"") client.run("create . danimtb/testing") self.assertIn("Repo origin deduced by 'auto': https://github.com/fake/fake.git", client.out) client.run("upload test/1.0@danimtb/testing -r upload_repo") self.assertNotIn("Uploading conan_sources.tgz", client.out) rev = servers["upload_repo"].server_store.get_last_revision(ref).revision ref = ref.copy_with_rev(rev) export_sources_path = os.path.join(servers["upload_repo"].server_store.export(ref), "conan_sources.tgz") self.assertFalse(os.path.exists(export_sources_path))
def export_pkg(self, path, name, channel, source_folder=None, build_folder=None, install_folder=None, profile_name=None, settings=None, options=None, env=None, force=False, user=None, version=None, cwd=None): settings = settings or [] options = options or [] env = env or [] cwd = cwd or os.getcwd() # Checks that info files exists if the install folder is specified if install_folder and not existing_info_files(_make_abs_path(install_folder, cwd)): raise ConanException("The specified --install-folder doesn't contain '%s' and '%s' " "files" % (CONANINFO, BUILD_INFO)) build_folder = _make_abs_path(build_folder, cwd) install_folder = _make_abs_path(install_folder, cwd, default=build_folder) source_folder = _make_abs_path(source_folder, cwd, default=build_folder) # Checks that no both settings and info files are specified if install_folder and existing_info_files(install_folder) and \ (profile_name or settings or options or env): raise ConanException("%s and %s are found, at '%s' folder, so specifying profile, " "settings, options or env is not allowed" % (CONANINFO, BUILD_INFO, install_folder)) infos_present = existing_info_files(install_folder) if not infos_present: profile = profile_from_args(profile_name, settings, options, env=env, cwd=cwd, client_cache=self._client_cache) else: profile = read_conaninfo_profile(install_folder) conanfile_path = _get_conanfile_path(path, cwd, py=True) conanfile = load_conanfile_class(conanfile_path) if (name and conanfile.name and conanfile.name != name) or \ (version and conanfile.version and conanfile.version != version): raise ConanException("Specified name/version doesn't match with the " "name/version in the conanfile") self._manager.export(conanfile_path, name, version, user, channel) if not (name and version): name = conanfile.name version = conanfile.version reference = ConanFileReference(name, version, user, channel) self._manager.export_pkg(reference, source_folder=source_folder, build_folder=build_folder, install_folder=install_folder, profile=profile, force=force)
def setUp(self): conanfile = textwrap.dedent(""" import os from conans import ConanFile, tools class TestConan(ConanFile): def package(self): folder_path = os.path.join(self.package_folder, "one_folder") tools.mkdir(folder_path) link_folder_path = os.path.join(self.package_folder, "other_folder") with tools.chdir(os.path.dirname(folder_path)): os.symlink(os.path.basename(folder_path), link_folder_path) """) self.ref = ConanFileReference("name", "version", "user", "channel") self.client = TurboTestClient() self.client.create(self.ref, conanfile)
def check_casing_conflict(cache, ref): # Check for casing conflict # Maybe a platform check could be added, but depends on disk partition refs = search_recipes(cache, ref, ignorecase=True) refs2 = [ ConanFileReference(r.name, r.version, r.user if ref.user else None, r.channel if ref.channel else None, validate=False) for r in refs ] if refs and ref not in refs2: raise ConanException( "Cannot export package with same name but different case\n" "You exported '%s' but already existing '%s'" % (str(ref), " ".join(str(s) for s in refs)))
def get_conanfile_upload_urls(name, version, username, channel, auth_user): """ Get a dict with all files and the upload url """ conan_service = ConanService(app.authorizer, app.server_store, auth_user) reference = ConanFileReference(name, version, username, channel) reader = codecs.getreader("utf-8") filesizes = json.load(reader(request.body)) urls = conan_service.get_conanfile_upload_urls( reference, filesizes) urls_norm = { filename.replace("\\", "/"): url for filename, url in urls.items() } return urls_norm
def load_consumer(self, conanfile_path, profile_host, name=None, version=None, user=None, channel=None, lock_python_requires=None, require_overrides=None): """ loads a conanfile.py in user space. Might have name/version or not """ conanfile = self.load_named(conanfile_path, name, version, user, channel, lock_python_requires) ref = ConanFileReference(conanfile.name, conanfile.version, user, channel, validate=False) if str(ref): conanfile.display_name = "%s (%s)" % ( os.path.basename(conanfile_path), str(ref)) else: conanfile.display_name = os.path.basename(conanfile_path) conanfile.output.scope = conanfile.display_name conanfile.in_local_cache = False try: conanfile.develop = True self._initialize_conanfile(conanfile, profile_host) # The consumer specific profile_host.user_options.descope_options(conanfile.name) conanfile.options.initialize_upstream(profile_host.user_options, name=conanfile.name) profile_host.user_options.clear_unscoped_options() if require_overrides is not None: for req_override in require_overrides: req_override = ConanFileReference.loads(req_override) conanfile.requires.override(req_override) return conanfile except ConanInvalidConfiguration: raise except Exception as e: # re-raise with file name raise ConanException("%s: %s" % (conanfile_path, str(e)))
def load_export(self, conanfile_path, name, version, user, channel, lock_python_requires=None): """ loads the conanfile and evaluates its name, version, and enforce its existence """ conanfile = self.load_named(conanfile_path, name, version, user, channel, lock_python_requires) if not conanfile.name: raise ConanException("conanfile didn't specify name") if not conanfile.version: raise ConanException("conanfile didn't specify version") if os.environ.get(CONAN_V2_MODE_ENVVAR, False): conanfile.version = str(conanfile.version) ref = ConanFileReference(conanfile.name, conanfile.version, user, channel) conanfile.display_name = str(ref) conanfile.output.scope = conanfile.display_name return conanfile
def get_conanfile_download_urls(name, version, username, channel, auth_user): """ Get a dict with all files and the download url """ conan_service = ConanService(app.authorizer, app.server_store, auth_user) ref = ConanFileReference(name, version, username, channel) try: urls = conan_service.get_conanfile_download_urls(ref) except NotFoundException: raise RecipeNotFoundException(ref) urls_norm = { filename.replace("\\", "/"): url for filename, url in urls.items() } return urls_norm
def get_conanfile_upload_urls(conanname, version, username, channel, auth_user): """ Get a dict with all files and the upload url """ conan_service = ConanService(app.authorizer, app.file_manager, auth_user) reference = ConanFileReference(conanname, version, username, channel) filesizes = json.load(request.body) urls = conan_service.get_conanfile_upload_urls( reference, filesizes) urls_norm = { filename.replace("\\", "/"): url for filename, url in urls.iteritems() } return urls_norm
def resolve(self, require, base_conanref, update, remote_name): version_range = require.version_range if version_range is None: return if require.is_resolved: ref = require.conan_reference resolved = self._resolve_version(version_range, [ref]) if not resolved: raise ConanException( "Version range '%s' required by '%s' not valid for " "downstream requirement '%s'" % (version_range, base_conanref, str(ref))) else: self._result.append( "Version range '%s' required by '%s' valid for " "downstream requirement '%s'" % (version_range, base_conanref, str(ref))) return ref = require.conan_reference # The search pattern must be a string search_ref = str( ConanFileReference(ref.name, "*", ref.user, ref.channel)) if update: resolved = (self._resolve_remote(search_ref, version_range, remote_name) or self._resolve_local(search_ref, version_range)) else: resolved = (self._resolve_local(search_ref, version_range) or self._resolve_remote(search_ref, version_range, remote_name)) if resolved: self._result.append( "Version range '%s' required by '%s' resolved to '%s'" % (version_range, base_conanref, str(resolved))) require.conan_reference = resolved else: base_conanref = base_conanref or "PROJECT" raise ConanException( "Version range '%s' from requirement '%s' required by '%s' " "could not be resolved" % (version_range, require, base_conanref))
def copy(self, reference, package_ids, username, channel, force=False): assert (isinstance(reference, ConanFileReference)) dest_ref = ConanFileReference(reference.name, reference.version, username, channel) # Copy export export_origin = self._paths.export(reference) if not os.path.exists(export_origin): raise ConanException("'%s' doesn't exist" % str(reference)) export_dest = self._paths.export(dest_ref) if os.path.exists(export_dest): if not force and not self._user_io.request_boolean( "'%s' already exist. Override?" % str(dest_ref)): return rmdir(export_dest) shutil.copytree(export_origin, export_dest) self._user_io.out.info("Copied %s to %s" % (str(reference), str(dest_ref))) export_sources_origin = self._paths.export_sources( reference, self._short_paths) export_sources_dest = self._paths.export_sources( dest_ref, self._short_paths) if os.path.exists(export_sources_dest): rmdir(export_sources_dest) shutil.copytree(export_sources_origin, export_sources_dest) self._user_io.out.info("Copied sources %s to %s" % (str(reference), str(dest_ref))) # Copy packages for package_id in package_ids: package_origin = PackageReference(reference, package_id) package_dest = PackageReference(dest_ref, package_id) package_path_origin = self._paths.package(package_origin, self._short_paths) package_path_dest = self._paths.package(package_dest, self._short_paths) if os.path.exists(package_path_dest): if not force and not self._user_io.request_boolean( "Package '%s' already exist." " Override?" % str(package_id)): continue rmdir(package_path_dest) shutil.copytree(package_path_origin, package_path_dest) self._user_io.out.info("Copied %s to %s" % (str(package_id), str(dest_ref)))
def _export_upload(self, name=0, version=None, deps=None, msg=None, static=True): dll_export = self.client.default_compiler_visual_studio and not static files = cpp_hello_conan_files(name, version, deps, msg=msg, static=static, private_includes=True, dll_export=dll_export) conan_ref = ConanFileReference(name, version, "lasote", "stable") self.client.save(files, clean_first=True) self.client.run("export lasote/stable") self.client.run("upload %s" % str(conan_ref))
def loads(text): graph_json = json.loads(text) profile = graph_json["profile"] # FIXME: Reading private very ugly profile, _ = _load_profile(profile, None, None) try: options = graph_json["options"] except KeyError: options = None else: options = OptionsValues(options) root = graph_json["root"] root_ref = ConanFileReference(root["name"], root["version"], root["user"], root["channel"], validate=False) return GraphInfo(profile=profile, options=options, root_ref=root_ref)
def _copy_to_server(self, client_store_path, server_store): subdirs = list_folder_subdirs(basedir=client_store_path.store, level=4) refs = [ConanFileReference(*folder.split("/"), revision=DEFAULT_REVISION_V1) for folder in subdirs] for ref in refs: origin_path = client_store_path.export(ref) dest_path = server_store.export(ref) shutil.copytree(origin_path, dest_path) server_store.update_last_revision(ref) packages = client_store_path.packages(ref) if not os.path.exists(packages): continue for package in os.listdir(packages): pid = PackageReference(ref, package, DEFAULT_REVISION_V1) origin_path = client_store_path.package(pid) dest_path = server_store.package(pid) shutil.copytree(origin_path, dest_path) server_store.update_last_package_revision(pid)
def resolve(self, require, base_conanref, update): version_range = require.version_range if version_range is None: return if require.is_resolved: ref = require.conan_reference resolved = self._resolve_version(version_range, [ref]) if not resolved: raise ConanException( "Version range '%s' required by '%s' not valid for " "downstream requirement '%s'" % (version_range, base_conanref, str(ref))) else: self._output.success( "Version range '%s' required by '%s' valid for " "downstream requirement '%s'" % (version_range, base_conanref, str(ref))) return ref = require.conan_reference # The search pattern must be a string search_ref = str( ConanFileReference(ref.name, "*", ref.user, ref.channel)) if update: searches = (self._resolve_remote, self._resolve_local) else: searches = (self._resolve_local, self._resolve_remote) for fcn in searches: resolved = fcn(search_ref, version_range) if resolved: break if resolved: self._output.success( "Version range '%s' required by '%s' resolved to '%s'" % (version_range, base_conanref, str(resolved))) require.conan_reference = resolved else: raise ConanException( "The version in '%s' from requirement '%s' could not be resolved" % (version_range, require))
def test_combined(self): base_folder = temp_folder() source_folder = os.path.join(base_folder, "source") conanfile_folder = os.path.join(base_folder, "conan") current_folder = os.path.join(base_folder, "current") os.makedirs(current_folder) client = TestClient(current_folder=current_folder) files = cpp_hello_conan_files("Hello0", "0.1") conan_ref = ConanFileReference("Hello0", "0.1", "lasote", "stable") conanfile = files.pop("conanfile.py") client.save(files, path=source_folder) conanfile = conanfile.replace("exports = '*'", 'exports = "../source*"') client.save({"conanfile.py": conanfile}, path=conanfile_folder) client.run("export lasote/stable --path=../conan") reg_path = client.paths.export(conan_ref) manif = FileTreeManifest.loads( load(client.paths.digestfile_conanfile(conan_ref))) self.assertIn( '%s: A new conanfile.py version was exported' % str(conan_ref), client.user_io.out) self.assertIn('%s: Folder: %s' % (str(conan_ref), reg_path), client.user_io.out) self.assertTrue(os.path.exists(reg_path)) for name in [ 'conanfile.py', 'conanmanifest.txt', 'source/main.cpp', 'source/executable', 'source/hello.cpp', 'source/CMakeLists.txt', 'source/helloHello0.h' ]: self.assertTrue(os.path.exists(os.path.join(reg_path, name))) expected_sums = { 'source/hello.cpp': '4f005274b2fdb25e6113b69774dac184', 'source/main.cpp': '0479f3c223c9a656a718f3148e044124', 'source/CMakeLists.txt': '52546396c42f16be3daf72ecf7ab7143', 'conanfile.py': '3ac566eb5b2e4df4417003f0e606e237', 'source/executable': '68b329da9893e34099c7d8ad5cb9c940', 'source/helloHello0.h': '9448df034392fc8781a47dd03ae71bdd' } self.assertEqual(expected_sums, manif.file_sums)
def test_code_sibling(self): # if provided a path with slash, it will use as a export base client = TestClient() conanfile = """ from conans import ConanFile class TestConan(ConanFile): name = "Hello" version = "1.2" exports = "../sibling/*.txt" """ files = {"recipe/conanfile.py": conanfile, "sibling/file.txt": "Hello World!"} client.save(files) client.current_folder = os.path.join(client.current_folder, "recipe") client.run("export . lasote/stable") ref = ConanFileReference("Hello", "1.2", "lasote", "stable") export_path = client.cache.package_layout(ref).export() content = load(os.path.join(export_path, "file.txt")) self.assertEqual("Hello World!", content)
def test_code_parent(self): # when referencing the parent, the relative folder "sibling" will be kept base = """ from conans import ConanFile class TestConan(ConanFile): name = "Hello" version = "1.2" exports = "../*.txt" """ for conanfile in (base, base.replace("../*.txt", "../sibling*")): client = TestClient() client.save({"recipe/conanfile.py": conanfile, "sibling/file.txt": "Hello World!"}) client.current_folder = os.path.join(client.current_folder, "recipe") client.run("export . lasote/stable") ref = ConanFileReference("Hello", "1.2", "lasote", "stable") export_path = client.cache.package_layout(ref).export() content = load(os.path.join(export_path, "sibling/file.txt")) self.assertEqual("Hello World!", content)