def upload_sftp(input_path, output_path, rm_remote=False): ssh_config = StatikConfig(input_path).remote['sftp'] t = paramiko.Transport((ssh_config['server'], 22)) t.connect(username=ssh_config['username'], password=ssh_config['password']) logger.info('Connecting to %s...' % ssh_config['server']) sftp = paramiko.SFTPClient.from_transport(t) logger.info('Connected to %s...' % ssh_config['server']) if rm_remote: path = ssh_config['dir-base'] + ssh_config['dir-root'] rm_r(sftp, path) for root, dirs, files in os.walk(output_path): for f in files: localpath = root + '/' + f dirname = root.replace(output_path, ssh_config['dir-root'], 1) remotepath = dirname + '/' + f logger.info('Publishing: %s' % (ssh_config['dir-base'] + remotepath)) try: mkdir_p(sftp, ssh_config['dir-base'] + dirname) except: pass sftp.put(localpath, ssh_config['dir-base'] + remotepath) sftp.close() logger.info('Connection closed.')
def watch(project_path, output_path, host='0.0.0.0', port=8000, min_reload_time=2.0): """Watches the given project path for filesystem changes, and automatically rebuilds the project when changes are detected. Also serves an HTTP server on the given host/port. Args: project_path: The path to the Statik project to be watched. output_path: The path into which to write the output files. host: The host IP/hostname to which to bind when serving output files. port: The port to which to bind when serving output files. min_reload_time: The minimum time (in seconds) between reloads when files change. """ _project_path, config_file = get_project_config_file(project_path, StatikProject.CONFIG_FILE) watcher = StatikWatcher(config_file, output_path, min_reload_time=min_reload_time) # generate once-off before starting the server watcher.generate() config = StatikConfig(config_file) watch_folders = [ StatikProject.MODELS_DIR, StatikProject.DATA_DIR, StatikProject.TEMPLATES_DIR, StatikProject.VIEWS_DIR, StatikProject.TEMPLATETAGS_DIR, config.assets_src_path, ] watch_folders = [f if os.path.isabs(f) else os.path.join(_project_path, f) for f in watch_folders] server = Server() for f in watch_folders: server.watch(f, func=watcher.generator_factory(f)) logger.info("Serving content from %s at http://%s:%d" % (output_path, host, port)) server.serve(root=output_path, host=host, port=port) logger.info("Stopped server")
def test_empty_config(self): config = StatikConfig(from_string="") self.assertEqual("Untitled project", config.project_name) self.assertEqual("/", config.base_path) self.assertEqual("assets", config.assets_src_path) self.assertEqual("assets", config.assets_dest_path) self.assertIsNone(config.theme)
def test_string_config(self): config = StatikConfig(from_string=TEST_CONFIG) self.assertEqual("Test Project", config.project_name) self.assertEqual("/blog/", config.base_path) self.assertEqual("src_static", config.assets_src_path) self.assertEqual("dest_static", config.assets_dest_path) self.assertEqual('The global value', config.context_static['some_project_var']) self.assertEqual('session.query(User).filter(User.active == True).all()', config.context_dynamic['users'])
def test_file_config(self): test_path = os.path.dirname(os.path.realpath(__file__)) config = StatikConfig( os.path.join(test_path, os.pardir, 'integration', 'data-simple', 'config.yml')) self.assertEqual("Unit Test Project", config.project_name) self.assertEqual("/", config.base_path) self.assertIsNone(config.theme)
def test_markdown_extension_config(self): config = StatikConfig(from_string=TEST_MARKDOWN_EXTENSIONS_CONFIG) self.assertEqual("Test Project", config.project_name) self.assertEqual("/", config.base_path) # check for our custom markdown extension config expected_extensions = copy(MarkdownConfig.DEFAULT_MARKDOWN_EXTENSIONS) expected_extensions.append("markdown.extensions.codehilite") self.assertEqual(expected_extensions, config.markdown_config.extensions) self.assertEqual({"markdown.extensions.toc": {"permalink": True}}, config.markdown_config.extension_config)
def test_string_config(self): config = StatikConfig(from_string=TEST_CONFIG) self.assertEqual("Test Project", config.project_name) self.assertEqual("/blog/", config.base_path) self.assertEqual("src_static", config.assets_src_path) self.assertEqual("dest_static", config.assets_dest_path) self.assertEqual('The global value', config.context_static['some_project_var']) self.assertEqual('session.query(User).filter(User.active == True).all()', config.context_dynamic['users']) self.assertIsNone(config.theme) self.assertFalse(config.markdown_config.enable_permalinks) self.assertIsNone(config.markdown_config.permalink_class) self.assertIsNone(config.markdown_config.permalink_title)
def test_markdown_config(self): config = StatikConfig(from_string=TEST_MARKDOWN_CONFIG) self.assertEqual("Test Project", config.project_name) self.assertEqual("/blog/", config.base_path) self.assertEqual("src_static", config.assets_src_path) self.assertEqual("dest_static", config.assets_dest_path) self.assertEqual('The global value', config.context_static['some_project_var']) self.assertEqual('session.query(User).filter(User.active == True).all()', config.context_dynamic['users']) self.assertIsNone(config.theme) # now check the markdown config self.assertTrue(config.markdown_config.enable_permalinks) self.assertEqual("permalink", config.markdown_config.permalink_class) self.assertEqual("Permalink to this heading", config.markdown_config.permalink_title) # check the default markdown extensions self.assertEqual(MarkdownConfig.DEFAULT_MARKDOWN_EXTENSIONS, config.markdown_config.extensions)
def test_themed_config(self): config = StatikConfig(from_string=TEST_THEMED_CONFIG) self.assertEqual("Themed Test Project", config.project_name) self.assertEqual("mytheme", config.theme)
def test_invalid_config(self): with self.assertRaises(MissingParameterError): StatikConfig()
def generate(self, output_path=None, in_memory=False): """Executes the Statik project generator. Args: output_path: The path to which to write output files. in_memory: Whether or not to generate the results in memory. If True, this will generate the output result as a dictionary. If False, this will write the output to files in the output_path. Returns: If in_memory is True, this returns a dictionary containing the actual generated static content. If in_memory is False, this returns an integer indicating the number of files generated in the output path. """ result = dict() if in_memory else 0 try: if output_path is None and not in_memory: raise ValueError( "If project is not to be generated in-memory, an output path must be specified" ) self.config = self.config or StatikConfig(self.config_file_path) if self.config.encoding is not None: logger.debug("Using encoding: %s" % self.config.encoding) else: logger.debug("Using encoding: %s" % self.config.encoding) self.models = self.load_models() self.template_engine = StatikTemplateEngine(self) self.views = self.load_views() if len(self.views) == 0: raise NoViewsError("Project has no views configured") self.db = self.load_db_data(self.models) self.project_context = self.load_project_context() in_memory_result = self.process_views() if in_memory: result = in_memory_result else: # dump the in-memory output to files file_count = self.dump_in_memory_result( in_memory_result, output_path) logger.info('Wrote %d output file(s) to folder: %s' % (file_count, output_path)) # copy any assets across, recursively self.copy_assets(output_path) result = file_count finally: try: # make sure to destroy the database engine (to provide for the possibility of database engine # reloads when watching for changes) if self.db is not None: self.db.shutdown() except Exception as e: logger.exception("Unable to clean up properly: %s" % e) return result
def generate(self, output_path=None, in_memory=False): """Executes the Statik project generator. Args: output_path: The path to which to write output files. in_memory: Whether or not to generate the results in memory. If True, this will generate the output result as a dictionary. If False, this will write the output to files in the output_path. Returns: If in_memory is True, this returns a dictionary containing the actual generated static content. If in_memory is False, this returns an integer indicating the number of files generated in the output path. """ result = dict() if in_memory else 0 logger.info("Generating Statik build...") try: if output_path is None and not in_memory: raise InternalError( "If project is not to be generated in-memory, an output path must be specified" ) self.error_context.update(filename=self.config_file_path) self.config = self.config or StatikConfig(self.config_file_path) if self.config.encoding is not None: logger.debug("Using encoding: %s", self.config.encoding) else: logger.debug("Using encoding: %s", self.config.encoding) self.error_context.clear() self.models = self.load_models() self.template_engine = StatikTemplateEngine(self) self.views = self.load_views() if not self.views: raise NoViewsError() self.db = self.load_db_data(self.models) self.project_context = self.load_project_context() in_memory_result = self.process_views() if in_memory: result = in_memory_result else: # dump the in-memory output to files file_count = self.dump_in_memory_result(in_memory_result, output_path) logger.info('Wrote %d output file(s) to folder: %s', file_count, output_path) # copy any assets across, recursively self.copy_assets(output_path) result = file_count logger.info("Success!") except StatikError as exc: logger.debug(traceback.format_exc()) logger.error(exc.render()) # re-raise the error to stop execution raise exc except Exception as exc: logger.debug(traceback.format_exc()) _exc = StatikError( message="Failed to build project. Run Statik in verbose mode (-v) to see " + "additional traceback information about this error.", orig_exc=exc, context=self.error_context ) logger.error(_exc.render()) raise _exc finally: try: # make sure to destroy the database engine (to provide for the possibility of # database engine reloads when watching for changes) if self.db is not None: self.db.shutdown() except Exception as e: logger.exception("Unable to clean up properly: %s", e) return result
def test_invalid_config(self): with self.assertRaises(ValueError): StatikConfig()