def render_to_dir(output_dir, urls_to_distill, stdout): mimes = {} load_urls(stdout) renderer = DistillRender(output_dir, urls_to_distill) for page_uri, file_name, http_response in renderer.render(): if file_name: local_uri = file_name full_path = os.path.join(output_dir, file_name) else: local_uri = page_uri if page_uri.startswith(os.sep): page_uri = page_uri[1:] full_path = os.path.join(output_dir, page_uri) try: content = http_response.content # .decode(settings.DEFAULT_CHARSET) # PATCH#1: utf-8 rendering correction except Exception as e: err = 'Failed to encode {} into {}: {}' DistillError(err.format(page_uri, settings.DEFAULT_CHARSET, e)) mime = http_response.get('Content-Type') renamed = ' (renamed from "{}")'.format(page_uri) if file_name else '' msg = 'Rendering page: {} -> {} ["{}", {} bytes] {}' stdout(msg.format(local_uri, full_path, mime, len(content), renamed)) try: dirname = os.path.dirname(full_path) if not os.path.isdir(dirname): os.makedirs(dirname) with open(full_path, 'wb') as f: # PATCH#2: utf-8 rendering correction f.write(content) except IOError as e: if e.errno == errno.EISDIR: err = ('Output path: {} is a directory! Try adding a ' '"distill_file" arg to your distill_url()') raise DistillError(err.format(full_path)) else: raise mimes[full_path] = mime.split(';')[0].strip() static_url = settings.STATIC_URL static_url = static_url[1:] if static_url.startswith('/') else static_url static_output_dir = os.path.join(output_dir, static_url) for file_from, file_to in renderer.copy_static(settings.STATIC_ROOT, static_output_dir): stdout('Copying static: {} -> {}'.format(file_from, file_to)) media_url = settings.MEDIA_URL media_url = media_url[1:] if media_url.startswith('/') else media_url media_output_dir = os.path.join(output_dir, media_url) for file_from, file_to in renderer.copy_static(settings.MEDIA_ROOT, media_output_dir): stdout('Copying media: {} -> {}'.format(file_from, file_to)) return True
def get_uri_values(self, func): try: v = func() except Exception as e: raise DistillError('Failed to call distill function: {}'.format(e)) if not v: return (None, ) elif isinstance(v, (list, tuple)): return v elif isinstance(v, types.GeneratorType): return list(v) else: err = 'Distill function returned an invalid type: {}' raise DistillError(err.format(type(v)))
def render_to_dir(output_dir, urls_to_distill, stdout): mimes = {} load_urls(stdout) renderer = DistillRender(output_dir, urls_to_distill) for page_uri, file_name, http_response in renderer.render(): if file_name: local_uri = file_name full_path = os.path.join(output_dir, file_name) else: local_uri = page_uri if page_uri.startswith('/'): page_uri = page_uri[1:] page_path = page_uri.replace('/', os.sep) full_path = os.path.join(output_dir, page_path) content = http_response.content mime = http_response.get('Content-Type') renamed = ' (renamed from "{}")'.format(page_uri) if file_name else '' msg = 'Rendering page: {} -> {} ["{}", {} bytes] {}' stdout(msg.format(local_uri, full_path, mime, len(content), renamed)) try: dirname = os.path.dirname(full_path) if not os.path.isdir(dirname): os.makedirs(dirname) with open(full_path, 'wb') as f: f.write(content) except IOError as e: if e.errno == errno.EISDIR: err = ('Output path: {} is a directory! Try adding a ' '"distill_file" arg to your distill_url()') raise DistillError(err.format(full_path)) else: raise mimes[full_path] = mime.split(';')[0].strip() return True
def distill_url(*a, **k): distill_func = k.get('distill_func') distill_file = k.get('distill_file') if distill_file: del k['distill_file'] if distill_func: del k['distill_func'] name = k.get('name') if not name: raise DistillError('Distill function provided with no name') if not callable(distill_func): err = 'Distill function not callable: {}' raise DistillError(err.format(distill_func)) urls_to_distill.append((distill_func, distill_file, name, a, k)) else: e = 'URL registered with distill_url but no distill function supplied' raise DistillWarning(e) return url(*a, **k)
def generate_uri(self, view_name, param_set): if isinstance(param_set, (list, tuple)): uri = reverse(view_name, args=param_set) elif isinstance(param_set, dict): uri = reverse(view_name, kwargs=param_set) else: err = 'Distill function returned an invalid type: {}' raise DistillError(err.format(type(param_set))) return uri
def render_view(self, uri, param_set, args): if len(args) < 2: raise DistillError('Invalid view arguments') view_regex, view_func = args[0], args[1] request_factory = RequestFactory() request = request_factory.get(uri) if isinstance(param_set, dict): a, k = (), param_set else: a, k = param_set, {} response = view_func(request, *a, **k) if self._is_str(response): response = HttpResponse(response) elif isinstance(response, TemplateResponse): response.render() if response.status_code != 200: err = 'View returned a non-200 status code: {}' raise DistillError(err.format(response.status_code)) return response
def render_view(self, uri, param_set, args): if len(args) < 2: raise DistillError('Invalid view arguments') view_regex, view_func = args[0], args[1] request_factory = RequestFactory() request = request_factory.get(uri) setattr(request, 'session', DummyInterface('request.session')) if isinstance(param_set, dict): a, k = (), param_set else: a, k = param_set, {} try: response = view_func(request, *a, **k) except Exception as err: e = 'Failed to render view "{}": {}'.format(uri, err) raise DistillError(e) from err if self._is_str(response): response = HttpResponse(response) elif isinstance(response, SimpleTemplateResponse): response.render() if response.status_code != 200: err = 'View returned a non-200 status code: {}' raise DistillError(err.format(response.status_code)) return response
def generate_uri(self, url, view_name, param_set): namespace = namespace_map.get(url, '') view_name_ns = namespace + ':' + view_name if namespace else view_name if isinstance(param_set, (list, tuple)): try: uri = reverse(view_name, args=param_set) except NoReverseMatch: uri = reverse(view_name_ns, args=param_set) elif isinstance(param_set, dict): try: uri = reverse(view_name, kwargs=param_set) except NoReverseMatch: uri = reverse(view_name_ns, args=param_set) else: err = 'Distill function returned an invalid type: {}' raise DistillError(err.format(type(param_set))) return uri
entry.url_patterns, namespace_path + [entry.namespace]) else: url_patterns_resolved += iter_resolved_urls( entry.url_patterns, namespace_path) else: url_patterns_resolved.append((namespace_path, entry)) return url_patterns_resolved for (namespaces, url) in iter_resolved_urls(urlconf.urlpatterns): if namespaces: nspath = ':'.join(namespaces) if url in namespace_map: raise DistillError( f'Ambiguous namespace for URL "{url}" in namespace ' f'"{nspath}", Distill does not support the same Django ' 'app being include()ed more than once in the same ' 'project') else: namespace_map[url] = nspath class DummyInterface: ''' Implements a dummy interface which raises a warning if any attributes or methods are accessed. This is used to replace specific non-implemented features, like sessions, which may be in end users site code but has no relevance for a static site and can be ignored. As this may be a non-obvious breaking change to a users site display a descriptive warning when rendering. '''
def distill_path(*args, **kwargs): err = ('Your installed version of Django ({}) does not supprt ' 'django.urls.path, please upgrade') raise DistillError(err.format(django_version))
def distill_url(*args, **kwargs): err = ('Your installed version of Django ({}) does not supprt ' 'django.urls.url or django.conf.urls.url, use ' 'django_distill.distill_re_path or django_distill.distill_path') raise DistillError(err.format(django_version))