<commit_before>import os
import re
import copy

import jinja2

from inifile import IniFile

from lektor.utils import tojson_filter, get_i18n_block
from lektor.context import url_to, site_proxy, get_ctx


DEFAULT_CONFIG = {
    'IMAGEMAGICK_EXECUTABLE': None,
    'LESSC_EXECUTABLE': None,
    'EPHEMERAL_RECORD_CACHE_SIZE': 500,
    'ATTACHMENT_TYPES': {
        '.jpg': 'image',
        '.jpeg': 'image',
        '.png': 'image',
        '.gif': 'image',
        '.tif': 'image',
        '.tiff': 'image',
        '.bmp': 'image',

        '.avi': 'video',
        '.mpg': 'video',
        '.mpeg': 'video',
        '.wmv': 'video',
        '.ogv': 'video',

        '.mp3': 'audio',
        '.wav': 'audio',
        '.ogg': 'audio',

        '.pdf': 'document',
        '.doc': 'document',
        '.docx': 'document',

        '.txt': 'text',
        '.log': 'text',
    },
    'SITE': {
        'name': None,
        'language': 'en',
    },
    'SERVERS': {}
}


def update_config_from_ini(config, inifile):
    def set_simple(target, source_path):
        rv = config.get(source_path)
        if rv is not None:
            config[target] = rv

    set_simple(target='IMAGEMAGICK_EXECUTABLE',
               source_path='env.imagemagick_executable')
    set_simple(target='LESSC_EXECUTABLE',
               source_path='env.lessc_executable')

    config['ATTACHMENT_TYPES'].update(
        (k.encode('ascii', 'replace'), v.encode('ascii', 'replace'))
        for k, v in inifile.section_as_dict('attachment_types'))

    config['SITE'].update(inifile.section_as_dict('site'))

    for sect in inifile.sections():
        if not sect.startswith('servers.'):
            continue
        server_id = sect.split('.')[1]
        config['SERVERS'][server_id] = inifile.section_as_dict(sect)


# Special files that should always be ignored.
IGNORED_FILES = ['thumbs.db', 'desktop.ini']

# These files are important for artifacts and must not be ignored when
# they are built even though they start with dots.
SPECIAL_SOURCES = ['_htaccess', '_htpasswd']
SPECIAL_ARTIFACTS = ['.htaccess', '.htpasswd']


class ServerInfo(object):

    def __init__(self, id, name_i18n, target, enabled=True):
        self.id = id
        self.name_i18n = name_i18n
        self.target = target
        self.enabled = enabled

    @property
    def name(self):
        return self.name_i18n.get('en')

    @property
    def short_target(self):
        match = re.match(r'([a-z]+)://([^/]+)', self.target)
        if match is not None:
            protocol, server = match.groups()
            return '%s via %s' % (server, protocol)
        return self.target

    def to_json(self):
        return {
            'id': self.id,
            'name': self.name,
            'target': self.target,
            'short_target': self.short_target,
            'enabled': self.enabled,
        }


class Expression(object):

    def __init__(self, env, expr):
        self.env = env
        self.tmpl = env.jinja_env.from_string('{{ __result__(%s) }}' % expr)

    def evaluate(self, pad=None, this=None, values=None):
        result = []
        def result_func(value):
            result.append(value)
            return u''
        values = self.env.make_default_tmpl_values(pad, this, values)
        values['__result__'] = result_func
        self.tmpl.render(values)
        return result[0]


class FormatExpression(object):

    def __init__(self, env, expr):
        self.env = env
        self.tmpl = env.jinja_env.from_string(expr)

    def evaluate(self, pad=None, this=None, values=None):
        values = self.env.make_default_tmpl_values(pad, this, values)
        return self.tmpl.render(values)


class CustomJinjaEnvironment(jinja2.Environment):

    def _load_template(self, name, globals):
        rv = jinja2.Environment._load_template(self, name, globals)
        ctx = get_ctx()
        if ctx is not None:
            ctx.record_dependency(rv.filename)
        return rv


class Config(object):

    def __init__(self, filename):
        self.filename = filename
        self.values = copy.deepcopy(DEFAULT_CONFIG)

        if os.path.isfile(filename):
            inifile = IniFile(filename)
            update_config_from_ini(self.values, inifile)

    def __getitem__(self, name):
        return self.values[name]

    @property
    def site_language(self):
        """The language of this site."""
        return self.values['SITE']['language']

    def get_servers(self, lang='en'):
        """Returns a list of servers (data translated to the given
        language).
        """
        rv = {}
        for server in self.values['SERVERS']:
            server_info = self.get_server(server, lang=lang)
            if server_info is None:
                continue
            rv[server] = server_info
        return rv

    def get_server(self, name, lang='en'):
        """Looks up a server info by name."""
        info = self.values['SERVERS'].get(name)
        if info is None:
            return None
        target = info.get('target')
        if target is None:
            return None
        return ServerInfo(
            id=name,
            name_i18n=get_i18n_block(info, 'name'),
            target=target,
            enabled=info.get('enabled', 'true').lower() in ('true', 'yes', '1'),
        )


class Environment(object):

    def __init__(self, root_path):
        self.root_path = os.path.abspath(root_path)

        self.config_filename = os.path.join(self.root_path, 'site.ini')

        self.jinja_env = CustomJinjaEnvironment(
            autoescape=self.select_jinja_autoescape,
            extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'],
            loader=jinja2.FileSystemLoader(
                os.path.join(self.root_path, 'templates'))
        )

        from lektor.db import F
        self.jinja_env.filters.update(
            tojson=tojson_filter,
            # By default filters need to be side-effect free.  This is not
            # the case for this one, so we need to make it as a dummy
            # context filter so that jinja2 will not inline it.
            url=jinja2.contextfilter(lambda ctx, *a, **kw: url_to(*a, **kw)),
        )
        self.jinja_env.globals.update(
            F=F,
            url_to=url_to,
            site=site_proxy,
        )

    @property<commit_msg>Added basic parsing of alternatives from the config<commit_after>import os
import re
import copy

import jinja2

from inifile import IniFile

from lektor.utils import tojson_filter, get_i18n_block
from lektor.context import url_to, site_proxy, get_ctx


DEFAULT_CONFIG = {
    'IMAGEMAGICK_EXECUTABLE': None,
    'LESSC_EXECUTABLE': None,
    'EPHEMERAL_RECORD_CACHE_SIZE': 500,
    'ATTACHMENT_TYPES': {
        '.jpg': 'image',
        '.jpeg': 'image',
        '.png': 'image',
        '.gif': 'image',
        '.tif': 'image',
        '.tiff': 'image',
        '.bmp': 'image',

        '.avi': 'video',
        '.mpg': 'video',
        '.mpeg': 'video',
        '.wmv': 'video',
        '.ogv': 'video',

        '.mp3': 'audio',
        '.wav': 'audio',
        '.ogg': 'audio',

        '.pdf': 'document',
        '.doc': 'document',
        '.docx': 'document',

        '.txt': 'text',
        '.log': 'text',
    },
    'SITE': {
        'name': None,
        'language': 'en',
    },
    'ALTERNATIVES': {},
    'PRIMARY_ALTERNATIVE': None,
    'SERVERS': {}
}


def update_config_from_ini(config, inifile):
    def set_simple(target, source_path):
        rv = config.get(source_path)
        if rv is not None:
            config[target] = rv

    set_simple(target='IMAGEMAGICK_EXECUTABLE',
               source_path='env.imagemagick_executable')
    set_simple(target='LESSC_EXECUTABLE',
               source_path='env.lessc_executable')

    config['ATTACHMENT_TYPES'].update(
        (k.encode('ascii', 'replace'), v.encode('ascii', 'replace'))
        for k, v in inifile.section_as_dict('attachment_types'))

    config['SITE'].update(inifile.section_as_dict('site'))

    for sect in inifile.sections():
        if sect.startswith('servers.'):
            server_id = sect.split('.')[1]
            config['SERVERS'][server_id] = inifile.section_as_dict(sect)
        elif sect.startswith('alternatives.'):
            alt = sect.split('.')[1]
            config['ALTERNATIVES'][alt] = {
                'name': get_i18n_block(inifile, 'alternatives.%s.name' % alt),
                'url_prefix': inifile.get('alternatives.%s.url_prefix' % alt),
                'primary': inifile.get_bool('alternatives.%s.primary' % alt),
            }

    for alt, alt_data in config['ALTERNATIVES'].iteritems():
        if alt_data['primary']:
            config['PRIMARY_ALTERNATIVE'] = alt
            break
    else:
        if config['ALTERNATIVES']:
            raise RuntimeError('Alternatives defined but no primary set.')


# Special files that should always be ignored.
IGNORED_FILES = ['thumbs.db', 'desktop.ini']

# These files are important for artifacts and must not be ignored when
# they are built even though they start with dots.
SPECIAL_SOURCES = ['_htaccess', '_htpasswd']
SPECIAL_ARTIFACTS = ['.htaccess', '.htpasswd']


class ServerInfo(object):

    def __init__(self, id, name_i18n, target, enabled=True):
        self.id = id
        self.name_i18n = name_i18n
        self.target = target
        self.enabled = enabled

    @property
    def name(self):
        return self.name_i18n.get('en')

    @property
    def short_target(self):
        match = re.match(r'([a-z]+)://([^/]+)', self.target)
        if match is not None:
            protocol, server = match.groups()
            return '%s via %s' % (server, protocol)
        return self.target

    def to_json(self):
        return {
            'id': self.id,
            'name': self.name,
            'target': self.target,
            'short_target': self.short_target,
            'enabled': self.enabled,
        }


class Expression(object):

    def __init__(self, env, expr):
        self.env = env
        self.tmpl = env.jinja_env.from_string('{{ __result__(%s) }}' % expr)

    def evaluate(self, pad=None, this=None, values=None):
        result = []
        def result_func(value):
            result.append(value)
            return u''
        values = self.env.make_default_tmpl_values(pad, this, values)
        values['__result__'] = result_func
        self.tmpl.render(values)
        return result[0]


class FormatExpression(object):

    def __init__(self, env, expr):
        self.env = env
        self.tmpl = env.jinja_env.from_string(expr)

    def evaluate(self, pad=None, this=None, values=None):
        values = self.env.make_default_tmpl_values(pad, this, values)
        return self.tmpl.render(values)


class CustomJinjaEnvironment(jinja2.Environment):

    def _load_template(self, name, globals):
        rv = jinja2.Environment._load_template(self, name, globals)
        ctx = get_ctx()
        if ctx is not None:
            ctx.record_dependency(rv.filename)
        return rv


class Config(object):

    def __init__(self, filename):
        self.filename = filename
        self.values = copy.deepcopy(DEFAULT_CONFIG)

        if os.path.isfile(filename):
            inifile = IniFile(filename)
            update_config_from_ini(self.values, inifile)

    def __getitem__(self, name):
        return self.values[name]

    @property
    def site_language(self):
        """The language of this site."""
        return self.values['SITE']['language']

    def get_servers(self, lang='en'):
        """Returns a list of servers (data translated to the given
        language).
        """
        rv = {}
        for server in self.values['SERVERS']:
            server_info = self.get_server(server, lang=lang)
            if server_info is None:
                continue
            rv[server] = server_info
        return rv

    def get_server(self, name, lang='en'):
        """Looks up a server info by name."""
        info = self.values['SERVERS'].get(name)
        if info is None:
            return None
        target = info.get('target')
        if target is None:
            return None
        return ServerInfo(
            id=name,
            name_i18n=get_i18n_block(info, 'name'),
            target=target,
            enabled=info.get('enabled', 'true').lower() in ('true', 'yes', '1'),
        )

    def list_alternatives(self):
        return sorted(self.values['ALTERNATIVES'])

    @property
    def primary_alternative(self):
        return self.values['PRIMARY_ALTERNATIVE']


class Environment(object):

    def __init__(self, root_path):
        self.root_path = os.path.abspath(root_path)

        self.config_filename = os.path.join(self.root_path, 'site.ini')

        self.jinja_env = CustomJinjaEnvironment(
            autoescape=self.select_jinja_autoescape,
            extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'],
            loader=jinja2.FileSystemLoader(
                os.path.join(self.root_path, 'templates'))
        )

        from lektor.db import F
        self.jinja_env.filters.update(
            tojson=tojson_filter,
            # By default filters need to be side-effect free.  This is not
            # the case for this one, so we need to make it as a dummy
            # context filter so that jinja2 will not inline it.
            url=jinja2.contextfilter(lambda ctx, *a, **kw: url_to(*a, **kw)),
        )
        self.jinja_env.globals.update(
            F=F,
            url_to=url_to,
            site=site_proxy,
        )

    @property
{'fraction_non_alphanumeric': 0.12884968761484747, 'fraction_numerical': 0.004483645718485851, 'mean_word_length': 2.249582039646525, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6097226', 'n_tokens_mistral': 4336, 'n_tokens_neox': 4199, 'n_words': 959}