def test_subproject(self): proj = Project(self) sm_path = self._create_sitemap( 'sitemap.txt', 'index.markdown\n\tsubproject.json') index_path = self._create_md_file('index.markdown', '# Project') sub_sm_path = self._create_sitemap( 'subsitemap.txt', 'subindex.markdown') sub_index_path = self._create_md_file( 'subindex.markdown', '# Subproject') self._create_conf_file('subproject.json', {'index': sub_index_path, 'sitemap': sub_sm_path, 'project_name': 'subproject', 'project_version': '0.2'}) conf = Config({'sitemap': sm_path, 'index': index_path, 'project_name': 'test-project', 'project_version': '0.1', 'output': self._output_dir}) proj.parse_name_from_config(conf) proj.parse_config(conf) proj.setup() self.assertEqual(len(proj.tree.get_pages()), 2) proj.format(self.link_resolver, self.output)
def test_subproject_extra_assets(self): proj = Project(self) self.project = proj sm_path = self._create_sitemap('sitemap.txt', 'index.markdown\n\tsubproject.json') index_path = self._create_md_file('index.markdown', '# Project') sub_sm_path = self._create_sitemap('subsitemap.txt', 'subindex.markdown') sub_index_path = self._create_md_file('subindex.markdown', '# Subproject') sub_asset = self._create_md_file('subassets/fake_asset.md', 'Fakery') self._create_conf_file( 'subproject.json', { 'index': sub_index_path, 'sitemap': sub_sm_path, 'project_name': 'subproject', 'extra_assets': [os.path.dirname(sub_asset)], 'project_version': '0.2' }) extra_assets = self._create_md_file('extra_assets/fake_asset.md', 'Main fake') conf = Config({ 'sitemap': sm_path, 'index': index_path, 'project_name': 'test-project', 'project_version': '0.1', 'extra_assets': [os.path.dirname(extra_assets)], 'output': self._output_dir }) proj.parse_name_from_config(conf) proj.parse_config(conf, toplevel=True) proj.setup() self.assertEqual(len(proj.tree.get_pages()), 2) proj.format(self.link_resolver, self.output) proj.write_out(self.output) # FIXME: reenable with a different testing strategy # pylint: disable=pointless-string-statement '''
def test_basic(self): proj = Project(self) conf = Config({}) with self.assertRaises(ConfigError): proj.parse_name_from_config(conf) proj.parse_config(conf) sm_path = self._create_sitemap('sitemap.txt', 'index.markdown\n') conf = Config({'sitemap': sm_path}) with self.assertRaises(ConfigError): proj.parse_name_from_config(conf) proj.parse_config(conf) index_path = self._create_md_file('index.markdown', '# Project') conf = Config({'sitemap': sm_path, 'index': index_path, 'project_name': 'test-project', 'project_version': '0.1'}) proj.parse_name_from_config(conf) proj.parse_config(conf) self.assertDictEqual( {key: type(val) for key, val in proj.extensions.items()}, self.extension_classes) proj.setup() self.assertEqual(len(proj.tree.get_pages()), 1)
class Application(Configurable): """ Banana banana """ # pylint: disable=too-many-instance-attributes def __init__(self, extension_classes): self.extension_classes = OrderedDict( {CoreExtension.extension_name: CoreExtension}) for ext_class in extension_classes: self.extension_classes[ext_class.extension_name] = ext_class self.output = None self.private_folder = None self.database = None self.link_resolver = None self.dry = False self.hostname = None self.config = None self.project = None self.formatted_signal = Signal() self.__all_projects = {} @staticmethod def add_arguments(parser): parser.add_argument('--dry', help='Dry run, nothing will be output', dest='dry', action='store_true') parser.add_argument('--deps-file-dest', help='Where to output the dependencies file') parser.add_argument('--deps-file-target', help='Name of the dependencies target', default='doc.stamp.d') parser.add_argument("-o", "--output", action="store", dest="output", help="where to output the rendered " "documentation") parser.add_argument('--hostname', dest='hostname', action='store', help='Where the documentation will be hosted, ' 'for example <http://hotdoc.com>. When provided, ' 'an XML sitemap will be generated for SEO ' 'purposes.') def parse_config(self, config): self.config = config self.output = config.get_path('output') self.dry = config.get('dry') self.hostname = config.get('hostname') self.project = Project(self) self.project.parse_name_from_config(self.config) self.private_folder = os.path.abspath('hotdoc-private-%s' % self.project.sanitized_name) shutil.rmtree(self.private_folder, ignore_errors=True) self.project.parse_config(self.config, toplevel=True) self.__setup_private_folder() self.__setup_database() def run(self): """ Banana banana """ res = 0 self.project.setup() self.__retrieve_all_projects(self.project) self.link_resolver.get_link_signal.connect_after(self.__get_link_cb) self.project.format(self.link_resolver, self.output) self.project.write_out(self.output) # Generating an XML sitemap makes no sense without a hostname if self.hostname: self.project.write_seo_sitemap(self.hostname, self.output) self.link_resolver.get_link_signal.disconnect(self.__get_link_cb) self.formatted_signal(self) self.__persist() return res def __get_link_cb(self, link_resolver, name): url_components = urlparse(name) project = self.__all_projects.get(url_components.path) if not project: return None page = project.tree.root ext = project.extensions[page.extension_name] formatter = ext.formatter prefix = formatter.get_output_folder(page) ref = page.link.ref if url_components.fragment: ref += '#%s' % url_components.fragment return Link(os.path.join(prefix, ref), page.link.get_title(), None) def __retrieve_all_projects(self, project): self.__all_projects[project.project_name] = project for subproj in project.subprojects.values(): self.__retrieve_all_projects(subproj) def __persist(self): if self.dry: return info('Persisting database and private files', 'persisting') self.database.persist() self.__dump_deps_file(self.project) def finalize(self): """ Banana banana """ self.project.finalize() def __setup_private_folder(self): if os.path.exists(self.private_folder): if not os.path.isdir(self.private_folder): error('setup-issue', '%s exists but is not a directory' % self.private_folder) else: os.mkdir(self.private_folder) def __setup_database(self): self.database = Database(self.private_folder) self.link_resolver = LinkResolver(self.database) def __dump_project_deps_file(self, project, deps_file, empty_targets): for page in list(project.tree.get_pages().values()): if not page.generated: empty_targets.append(page.source_file) deps_file.write(u'%s ' % page.source_file) for subproj in project.subprojects.values(): self.__dump_project_deps_file(subproj, deps_file, empty_targets) def __dump_deps_file(self, project): dest = self.config.get('deps_file_dest', None) target = self.config.get('deps_file_target') if dest is None: info("Not dumping deps file") return info("Dumping deps file to %s with target %s" % (dest, target)) destdir = os.path.dirname(dest) if not os.path.exists(destdir): os.makedirs(destdir) empty_targets = [] with open(dest, 'w', encoding='utf-8') as _: _.write(u'%s: ' % target) for dep in self.config.get_dependencies(): empty_targets.append(dep) _.write(u'%s ' % dep.replace(' ', '\\ ')) self.__dump_project_deps_file(project, _, empty_targets) for empty_target in empty_targets: _.write(u'\n\n%s:' % empty_target)
class Application(Configurable): def __init__(self, extension_classes): self.extension_classes = OrderedDict( {CoreExtension.extension_name: CoreExtension}) for ext_class in extension_classes: self.extension_classes[ext_class.extension_name] = ext_class self.output = None self.private_folder = None self.change_tracker = None self.database = None self.link_resolver = None self.dry = False self.incremental = False self.config = None self.project = None self.formatted_signal = Signal() self.__all_projects = {} @staticmethod def add_arguments(parser): parser.add_argument('--dry', help='Dry run, nothing will be output', dest='dry', action='store_true') parser.add_argument('--deps-file-dest', help='Where to output the dependencies file') parser.add_argument('--deps-file-target', help='Name of the dependencies target', default='doc.stamp.d') parser.add_argument("-o", "--output", action="store", dest="output", help="where to output the rendered " "documentation") def parse_config(self, config): self.config = config self.output = config.get_path('output') self.dry = config.get('dry') self.project = Project(self) self.project.parse_name_from_config(self.config) self.private_folder = os.path.abspath('hotdoc-private-%s' % self.project.sanitized_name) self.__create_change_tracker(self.config.get('disable_incremental')) self.project.parse_config(self.config, toplevel=True) self.__setup_private_folder() self.__setup_database() def run(self): res = 0 if self.config.conf_file: self.change_tracker.add_hard_dependency(self.config.conf_file) self.project.setup() self.__retrieve_all_projects(self.project) self.link_resolver.get_link_signal.connect_after(self.__get_link_cb) self.project.format(self.link_resolver, self.output) self.project.write_out(self.output) self.link_resolver.get_link_signal.disconnect(self.__get_link_cb) self.formatted_signal(self) self.__persist(self.project) return res def __get_link_cb(self, link_resolver, name): url_components = urlparse(name) project = self.__all_projects.get(url_components.path) if not project: return None page = project.tree.root ext = project.extensions[page.extension_name] formatter = ext.formatter prefix = formatter.get_output_folder(page) ref = page.link.ref if url_components.fragment: ref += '#%s' % url_components.fragment return Link(os.path.join(prefix, ref), page.link.get_title(), None) def __retrieve_all_projects(self, project): self.__all_projects[project.project_name] = project for subproj in project.subprojects.values(): self.__retrieve_all_projects(subproj) def __dump_project_deps_file(self, project, deps_file, empty_targets): for page in list(project.tree.get_pages().values()): if not page.generated: empty_targets.append(page.source_file) deps_file.write(u'%s ' % page.source_file) for subproj in project.subprojects.values(): self.__dump_project_deps_file(subproj, deps_file, empty_targets) def __dump_deps_file(self, project): dest = self.config.get('deps_file_dest', None) target = self.config.get('deps_file_target') if dest is None: info("Not dumping deps file") return info("Dumping deps file to %s with target %s" % (dest, target)) destdir = os.path.dirname(dest) if not os.path.exists(destdir): os.makedirs(destdir) empty_targets = [] with open(dest, 'w', encoding='utf-8') as _: _.write(u'%s: ' % target) for dep in self.config.get_dependencies(): empty_targets.append(dep) _.write(u'%s ' % dep) self.__dump_project_deps_file(project, _, empty_targets) for empty_target in empty_targets: _.write(u'\n\n%s:' % empty_target) def __persist(self, project): if self.dry: return info('Persisting database and private files', 'persisting') project.persist() self.database.persist() with open(os.path.join(self.private_folder, 'change_tracker.p'), 'wb') as _: _.write(pickle.dumps(self.change_tracker)) self.__dump_deps_file(project) def finalize(self): if self.database is not None: info('Closing database') self.database.close() self.project.finalize() def __setup_private_folder(self): if os.path.exists(self.private_folder): if not os.path.isdir(self.private_folder): error('setup-issue', '%s exists but is not a directory' % self.private_folder) else: os.mkdir(self.private_folder) def __setup_database(self): self.database = Database() #self.database.comment_added_signal.connect(self.__add_default_tags) #self.database.comment_updated_signal.connect( # self.__add_default_tags) self.database.setup(self.private_folder) self.link_resolver = LinkResolver(self.database) def __create_change_tracker(self, disable_incremental): if not disable_incremental: try: with open( os.path.join(self.private_folder, 'change_tracker.p'), 'rb') as _: self.change_tracker = pickle.loads(_.read()) if self.change_tracker.hard_dependencies_are_stale(): raise IOError self.incremental = True info("Building incrementally") # pylint: disable=broad-except except Exception: pass if not self.incremental: info("Building from scratch") shutil.rmtree(self.private_folder, ignore_errors=True) self.change_tracker = ChangeTracker()