def run_gen(self): """Generates the static site.""" if not CONFIG.SITE.ENGINES: message = "All engines are inactive -- nothing to generate." console(message, is_bright=True, color='red') else: generator.run()
def create_widgets(self, engine=None): """Create widgets from engine or site.""" if engine is not None: try: widgets = engine.config.WIDGETS except AttributeError: return else: widgets = self.config.SITE.WIDGETS for widget in widgets: if engine is not None: message = "Creating engine widget: %s" % widget else: message = "Creating site widget: %s" % widget console(message) self.logger.debug(message) try: widget_func = getattr(self.widgets_mod, widget) except AttributeError: message = "Widget %s not found." % widget self.logger.error(message) self.logger.debug(format_exc()) raise if engine is not None: # engine widgets work on their engine instances self.widgets[widget] = widget_func(engine) else: # site widgets work on this site instance self.widgets[widget] = widget_func(self) self.logger.debug("created: %s widget" % widget)
def main(cli_arglist=None): """Main execution routine. cli_arglist -- List of arguments passed to the command line. """ session = Runner() try: cmd = session.build_parsers().parse_args(cli_arglist) # only build logger if we're not starting a new project # or just checking version if cmd.name not in ['demo', 'init', 'version']: session.build_logger() # attach parsed object to the package-wide config setattr(CONFIG, 'CMD', cmd) os.chdir(CONFIG.VOLT.ROOT_DIR) logger = logging.getLogger('main') logger.debug("running: %s" % cmd.name) cmd.run() except ConfigNotFoundError: message = "You can only run 'volt %s' inside a Volt project directory." % \ cmd.name console("Error: %s" % message, color='red', is_bright=True) console("Start a Volt project by running 'volt init' inside an empty directory.") if os.path.exists('volt.log'): os.remove('volt.log') sys.exit(1)
def run_demo(self): """Runs a quick demo of Volt.""" # copy demo files self.run_init(is_demo=True) console("\nPreparing your lightning-speed Volt tour...", is_bright=True) # need to pass arglist to serve, so we'll call main main(['serve'])
def run_init(self, is_demo=False): """Starts a new Volt project. init -- String, must be 'init' or 'demo', denotes which starting files will be copied into the current directory. """ cmd_name = 'init' if not is_demo else 'demo' dir_content = os.listdir(os.curdir) if dir_content != [] and dir_content != ['volt.log']: message = "'volt %s' must be run inside an empty directory." % cmd_name console("Error: %s" % message, color='red', is_bright=True) sys.exit(1) # get volt installation directory and demo dir target_path = os.path.join(os.path.dirname(__file__), 'templates', cmd_name) # we only need the first layer to do the copying parent_dir, child_dirs, top_files = os.walk(target_path).next() # copy all files in parent that's not a .pyc file for top in [x for x in top_files if not x.endswith('.pyc')]: shutil.copy2(os.path.join(parent_dir, top), os.curdir) # copy all child directories for child in child_dirs: shutil.copytree(os.path.join(parent_dir, child), child) if not is_demo: console("\nVolt project started. Have fun!\n", is_bright=True)
def run_serve(self): """Generates the static site, and if successful, runs the Volt server.""" self.run_gen() if not os.path.exists(CONFIG.VOLT.SITE_DIR): message = "Site directory not found -- nothing to serve." console(message, is_bright=True, color='red') else: server.run()
def prepare_output(self): """Copies the asset directory contents to site directory.""" message = "Preparing output directory: %s" % self.config.VOLT.SITE_DIR console(message) self.logger.debug(message) if os.path.exists(self.config.VOLT.SITE_DIR): shutil.rmtree(self.config.VOLT.SITE_DIR) shutil.copytree(self.config.VOLT.ASSET_DIR, self.config.VOLT.SITE_DIR, \ ignore=shutil.ignore_patterns(self.config.SITE.IGNORE_PATTERN))
def dispatch_engines(self): """Runs the engines' dispatch method.""" for engine in self.engines: message = "Dispatching %s engine to URL '%s'" % \ (engine.lower(), self.engines[engine].config.URL) console(message) self.logger.debug(message) # attach all widgets to each engine, so they're accessible in templates self.engines[engine].widgets = self.widgets # dispatch them self.engines[engine].dispatch()
def run_ext(self): """Adds template for engine, plugin, or widget.""" builtin = CONFIG.CMD.builtin template = CONFIG.CMD.template volt_dir = os.path.dirname(__file__) template_source = os.path.join(volt_dir, 'templates') if template == 'widget': # if template type is widget, only copy / create if it's not # present already if not os.path.exists(CONFIG.VOLT.USER_WIDGET): # if builtin is not an empty string, get the default widgets if builtin: builtin_dir = os.path.join(volt_dir, 'config') shutil.copy2(os.path.join(builtin_dir, 'default_widgets.py'), os.path.join(os.curdir, 'widgets.py')) # otherwise get the widget template else: shutil.copy2(os.path.join(template_source, 'widgets.py'), os.curdir) else: template_dir = os.path.join(os.getcwd(), template + 's') # create plugin / engine dir in the root dir # unless it's there already if not os.path.exists(template_dir): os.mkdir(template_dir) # if builtin is specified, try to get the builtin plugin/engine if builtin: builtin_dir = os.path.join(volt_dir, template, 'builtins') try: if builtin == 'atomic': shutil.copytree(os.path.join(builtin_dir, builtin), \ os.path.join(template_dir, builtin)) else: shutil.copy2(os.path.join(builtin_dir, builtin + '.py'), \ template_dir) except IOError: message = "Builtin %s '%s' not found." % (template, builtin) console("Error: %s" % message, color='red', is_bright=True) sys.exit(1) # otherwise copy the plugin/engine template else: template_file = template + '.py' if not os.path.exists(os.path.join(template_dir, template_file)): shutil.copy2(os.path.join(template_source, template_file), \ template_dir)
def run(): """Generates the site.""" logger = logging.getLogger('gen') sys.stdout.write("\n") message = "Volt %s Static Site Generator" % VERSION console(message, is_bright=True) logger.debug(message) # generate the site! start_time = time() Site().create() message = "Site generated in %.3fs" % (time() - start_time) console(message, color='yellow') logger.debug(message) sys.stdout.write('\n')
def run_plugins(self, engine=None): """Runs plugins on engine or site.""" if engine is not None: try: plugins = engine.config.PLUGINS except AttributeError: return else: plugins = self.config.SITE.PLUGINS for plugin in plugins: if engine is not None: message = "Running engine plugin: %s" % (plugin) else: message = "Running site plugin: %s" % plugin console(message) self.logger.debug(message) if not plugin in self.plugins: try: plugin_class = self.get_processor(plugin, 'plugins') except ImportError: message = "Plugin %s not found." % plugin self.logger.error(message) self.logger.debug(format_exc()) raise self.plugins[plugin] = plugin_class() self.plugins[plugin].prime() if engine is not None: # engine plugins work on their engine instances self.plugins[plugin].run(engine) else: # site plugins work on this site instance self.plugins[plugin].run(self) # attach plugin config values (if defined) for access in templates if self.plugins[plugin].USER_CONF_ENTRY is not None: setattr(self.config, self.plugins[plugin].USER_CONF_ENTRY, \ self.plugins[plugin].config) self.logger.debug("ran: %s plugin" % plugin)
def activate_engines(self): """Activates all engines according to the configurations. This method consists of four steps: 1. Engine priming: all engines listed in CONFIG.SITE.ENGINES are loaded. Any engines found in the user directory takes precedence over built-in engines. The default settings in each engine are then consolidated with the user's setting in voltconf.py to yield the final configurations that will be used in subsequent engine methods. 2. Engine preprocessing: all the engines' preprocess() method are then run. Any unit processing that happens before the plugins are run is done by the preprocess method. 3. Plugin run: plugins targeting each engine are run to process the the target engines' units. Similar to engines, plugins are also primed to consolidate the default and user configurations. 4. Widget creation: widgets for each engine are created and made accessible from the any templates. """ for engine_name in self.config.SITE.ENGINES: engine_class = self.get_processor(engine_name, 'engines') engine = engine_class() message = "Engine loaded: %s" % engine_name.capitalize() console(message, color='cyan') self.logger.debug(message) engine.prime() self.logger.debug('done: priming %s' % engine_class.__name__) engine.preprocess() self.logger.debug('done: preprocessing %s' % engine_class.__name__) self.run_plugins(engine) self.create_widgets(engine) self.engines[engine_name] = engine # attach engine config values for access in templates setattr(self.config, self.engines[engine_name].USER_CONF_ENTRY, \ self.engines[engine_name].config)
def github_search(site): """Site widget for returning github repo search, sorted on last push time. Example usage: {% for item in widgets.github_search %} <a href="{{ item.url }}">{{ item.name }} ({{ item.watchers }})</a> {% endfor %} """ import json try: #try python3 first from urllib.request import urlopen from urllib.parse import urlencode except ImportError: # fallback to python2 from urllib import urlencode, urlopen from datetime import datetime from volt.utils import console # set our search parameters query_string = 'static website' args = {'language': 'Python'} base_url = 'http://github.com/api/v2/json/repos/search/' # retrieve search results using urllib and json query = '%s%s' % (query_string.replace(' ', '+'), '?' + urlencode(args)) try: response = urlopen(base_url + query).read().decode('utf-8') except IOError: console("WARNING: github_search can not connect to the internet.\n", \ color='red', is_bright=True) return [] data = json.loads(response)['repositories'] # get repos with at least 10 watchers results = [repo for repo in data if repo['watchers'] >= 10] # finally, we'll sort our selection ~ most recent push time first def gettime(datestr, format="%Y/%m/%d %H:%M:%S"): return datetime.strptime(datestr[:-6], format) results.sort(key=lambda x: gettime(x['pushed_at']), reverse=True) return results
def write_site_pages(self): """Write site pages, such as a separate index.html or 404.html.""" for filename in self.config.SITE.PAGES: message = "Writing site page: '%s'" % filename console(message) self.logger.debug(message) template = self.config.SITE.TEMPLATE_ENV.get_template(filename) path = os.path.join(self.config.VOLT.SITE_DIR, filename) if os.path.exists(path): message = "File %s already exists. Make sure there are no "\ "other entries leading to this file path." % path console("Error: %s" % message, is_bright=True, color='red') self.logger.error(message) sys.exit(1) rendered = template.render(page={}, CONFIG=self.config, \ widgets=self.widgets) if sys.version_info[0] < 3: rendered = rendered.encode('utf-8') write_file(path, rendered)
def process_request(self, request, client_address): """Finishes one request by instantiating the handler class. Prior to handler class initialization, this method checks the latest timestamp of all directories inside the Volt project. If the result is higher than the prior timestamp (self.last_mtime), then the entire site will be regenerated. """ latest_mtime = self.check_dirs_mtime() if self.last_mtime < latest_mtime: message = "Source file modification detected -- regenerating site" console(message, color='yellow') self.logger.debug(message) self.last_mtime = latest_mtime CONFIG.reset() generator.run() self.logger.debug('done: regenerating site') ThreadingTCPServer.process_request(self, request, client_address)
def log_message(self, format, *args): # overrides parent log_message to provide a more compact output. message = format % args if int(args[1]) >= 400: console(message, color='red') elif int(args[1]) >= 300: console(message, color='cyan') else: console(message) self.logger.debug(message)
13: "You don't have permission to access port %s" % (CONFIG.CMD.server_port), 98: "Port %s already in use" % (CONFIG.CMD.server_port)} try: message = ERRORS[e.args[0]] except (AttributeError, KeyError): message = str(e) logger.error(message) sys.exit(1) run_address, run_port = server.socket.getsockname() if run_address == '127.0.0.1': run_address = 'localhost' message = "Volt %s Development Server" % VERSION console(message, is_bright=True) logger.debug(message) message = "Serving %s" % CONFIG.VOLT.SITE_DIR console(message) logger.debug(message) message = "Running at http://%s:%s" % (run_address, run_port) console(message) logger.debug(message) try: server.serve_forever() except KeyboardInterrupt: server.shutdown() finally:
def error(self, message): console("\nError: %s" % message, color='red', is_bright=True) self.print_usage() sys.stdout.write("\n") sys.exit(1)
def run_version(self): """Shows version number.""" console("Volt %s" % VERSION)
def run_version(self): """Shows version number.""" console("Volt %s" % __version__)