import os import percy try: # Python 3's pathname2url from urllib.request import pathname2url except ImportError: # Python 2's pathname2url from urllib import pathname2url from percy import utils try: from urllib.parse import urlparse except ImportError: from urlparse import urlparse __all__ = ['ResourceLoader'] MAX_FILESIZE_BYTES = 15 * 1024**2 # 15 MiB. class BaseResourceLoader(object): @property def build_resources(self): raise NotImplementedError('subclass must implement abstract method') @property def snapshot_resources(self): raise NotImplementedError('subclass must implement abstract method') class ResourceLoader(BaseResourceLoader): def __init__(self, root_dir=None, base_url=None, webdriver=None): self.root_dir = root_dir self.base_url = base_url if self.base_url and self.base_url.endswith(os.path.sep): self.base_url = self.base_url[:-1] # TODO: more separate loader subclasses and pull out Selenium-specific logic? self.webdriver = webdriver @property def build_resources(self): resources = [] if not self.root_dir: return resources for root, dirs, files in os.walk(self.root_dir, followlinks=True): for file_name in files: path = os.path.join(root, file_name) if os.path.getsize(path) > MAX_FILESIZE_BYTES: continue with open(path, 'rb') as f: content = f.read() path_for_url = pathname2url(path.replace(self.root_dir, '', 1)) if self.base_url[-1] == '/' and path_for_url[0] == '/': path_for_url = path_for_url.replace('/', '' , 1) resource_url = "{0}{1}".format(self.base_url, path_for_url) resource = percy.Resource( resource_url=resource_url, sha=utils.sha256hash(content), local_path=os.path.abspath(path), ) resources.append(resource) return resources @property def snapshot_resources(self): # Only one snapshot resource, the root page HTML. return [ percy.Resource( # Assumes a Selenium webdriver interface. resource_url=urlparse(self.webdriver.current_url).path, is_root=True, mimetype='text/html', content=self.webdriver.page_source, ) ]