Ejemplo n.º 1
0
 def _refmt_io(file_i, file_o='', *args, **kwargs):
     ext_i, ext_o = func.__name__.split('_2_')
     assert os.path.exists(file_i) and file_i.endswith(ext_i)
     if not file_o:
         file_o = '{}/{}.base64.{}'.format(
             os.path.dirname(file_i),
             os.path.splitext(os.path.basename(file_i))[0], ext_o)
     lk.logp(file_i, file_o)
     if os.path.exists(file_o):
         lk.loga('the target file already exists, it will be overriden')
     return func(file_i, file_o, *args, **kwargs)
Ejemplo n.º 2
0
    def compile_one(self, src_file, dst_file):
        """
        Compile `src_file` and generate `dst_file`.

        Args:
            src_file
            dst_file
            
        References:
            `cmd:pyarmor obfuscate -h`
        
        Results:
            the `dst_file` has the same content structure:
                from pytransform import pyarmor_runtime
                pyarmor_runtime()
                __pyarmor__(__name__, __file__, b'\\x50\\x59\\x41...')
            `pytransform` comes from `{dist}/lib`, it will be added to
            `sys.path` in the startup (see `pyportable_installer/template/
            bootloader.txt` and `pyportable_installer/no3_build_pyproject.py::
            func:_create_launcher`).
        
        Notes:
            table of `pyarmor obfuscate --bootstrap {0~4}`

            | command            | result                                      |
            | ================== | =========================================== |
            | `pyarmor obfuscate | each obfuscated file has a header of        |
            |  --bootstrap 0`    | `from .pytransform import pyarmor_runtime`  |
            | ------------------ | ------------------------------------------- |
            | `pyarmor obfuscate | only `__init__.py` has a header of          |
            |  --bootstrap 1`    | `from .pytransform import pyarmor_runtime`  |
            | ------------------ | ------------------------------------------- |
            | `pyarmor obfuscate | each obfuscated file has a header of        |
            |  --bootstrap 2`    | `from pytransform import pyarmor_runtime`   |
            |                    | **this is what we want**                    |
            | ------------------ | ------------------------------------------- |
            | `pyarmor obfuscate | *unknown*                                   |
            |  --bootstrap 3`    |                                             |
            | ------------------ | ------------------------------------------- |
            | `pyarmor obfuscate | *unknown*                                   |
            |  --bootstrap 4`    |                                             |
        """
        lk.loga('compiling', ospath.basename(src_file))

        if self._liscense == 'trial':
            # The limitation of content size is 32768 bytes (i.e. 32KB) in
            # pyarmor trial version.
            if (size := ospath.getsize(src_file)) > 32768:
                lk.logt(
                    '[W0357]', f'该文件: "{src_file}" 的体积 ({size}) 超出了 pyarmor 试用'
                    f'版的限制, 请购买个人版或商业版许可后重新编译! (本文件谨以'
                    f'源码形式打包)')
                copyfile(src_file, dst_file)
                return
Ejemplo n.º 3
0
 def __init__(self, pyml_text: str):
     self._keyx = 0  # keyx: mask holder key index
     self._mask = {}  # type: Hint.MaskHolder
     
     self._conflicts = re.compile(
         r'{mask_holder_\d+}|{mask_holder_conflict}'
     ).findall(pyml_text)
     if self._conflicts:
         lk.loga('Found {} conflicts'.format(len(self._conflicts)))
     
     self._holder_pattern = re.compile(r'{mask_holder_\d+}')
     self._text = self._holder_pattern.sub(
         '{mask_holder_conflict}', pyml_text
     )
Ejemplo n.º 4
0
 def plain_text(self):
     """
     E.g.
         # origin_text = 'a = "x and y" \\ \n    "and z"'
         self._text = 'a = {mask2}'
         self._mask = {
             'mask1': '\\ \n',
             'mask2': '"x and y" {mask1}    "and z"',
         }
         ->
             result = 'a = "x and y" \\ \n    "and z"'
             # assert result == origin_text
     """
     pattern = self._holder_pattern
     text = self._text
     _error_stack = []
     
     while pattern.search(text):
         _error_stack.append('---------------- ERROR STACK ----------------')
         _error_stack.append(text)
         
         try:
             for holder in set(pattern.findall(text)):
                 text = text.replace(
                     holder, self._mask[holder[1:-1]]
                     #   `holder[1:-1]` means `holder.strip("{}")`
                 )
         except Exception as e:
             from lk_utils import read_and_write
             read_and_write.dumps(
                 _error_stack, f1 := './pyml_composer_error.txt'
             )
             read_and_write.dumps(
                 self._mask, f2 := './pyml_composer_error.json'
             )
             raise Exception(
                 e, f'Plese check dumped info from [{f1}] and [{f2}] for '
                    f'more infomation.'
             )
     
     else:
         if text == self._text:
             lk.loga('No mask node found')
     
     for holder in self._conflicts:
         text = text.replace('{mask_holder_conflict}', holder, 1)
     
     del _error_stack
     return text
def main(conf: TConf):
    """ Create dist tree (all empty folders under dist root) """
    _precheck(conf)
    
    # create build_dir, lib_dir, src_dir
    mkdir(conf['build']['dist_dir'])
    mkdir(conf['build']['dist_dir'] + '/build')
    mkdir(conf['build']['dist_dir'] + '/lib')
    mkdir(conf['build']['dist_dir'] + '/src')
    
    dist_tree = DistTree()
    
    """
    Add to source dirs: 对相对路径敏感的目录需要保持原有的目录结构关系. 它们需要
    被加入到 source dirs. 如下所列 (见加号标识):
        pyproject
            build
                + proj_dir
                target
                    + file
                + attachments (partial, which value not includes 'dist:')
                
    Do not add to source dirs (见减号标识):
        pyproject
            build
                - dist_dir
                - icon
                - readme
                - attachments (partial, which value includes 'dist:')
                - module_paths
    """
    dist_tree.add_src_dirs(
        conf['build']['proj_dir'],
        conf['build']['target']['file'],
        *(k for k, v in conf['build']['attachments'].items()
          if 'dist:' not in v),
        # *(v for v in conf['build']['module_paths']
        #   if not v.startswith('dist:'))
    )
    
    src_root = dist_tree.suggest_src_root()
    lk.loga(src_root)
    dst_root = conf['build']['dist_dir']
    dist_tree.build_dst_dirs(src_root, f'{dst_root}/src')
    
    from .global_dirs import init_global_dirs
    init_global_dirs(src_root, f'{dst_root}/src')
    return src_root, dst_root
Ejemplo n.º 6
0
        def _recurse(tree: Hint.SourceTree):
            for node in tree.values():

                if node['field'] in pseudo_fields:
                    _recurse(node['children'])
                    continue

                for match in pattern.finditer(node['line_stripped']):
                    prop, oper, expr = \
                        match.group(1), match.group(2), match.group(3)
                    lk.loga('{:15}\t{:^5}\t{:<}'.format(prop, oper, expr))
                    #        ^A--^  ^B--^  ^C-^
                    #   A: 右对齐, 宽度 15; B: 居中, 宽度 5; C: 左对齐, 宽度不限

                    if expr:
                        """
                        说明遇到了这类情况 (示例):
                            width: height + 10
                        根据 pyml 语法要求, 单行的属性赋值, 不可以有子语法块, 也
                        就是说下面的情况是不允许的:
                            width: height + 10
                                if height + 10 > 10:
                                    return 10
                                else:
                                    return height
                        """
                        assert bool(node['children']) is False
                    else:
                        """
                        说明遇到了这类情况 (示例):
                            width:
                                if height > 10:
                                    return 10
                                else:
                                    return height
                        其中 prop = 'width', oper = ':', expr 捕获到的是 '', 但
                        其实应该取它的块结构. 所以下面我们就做这个工作.
                        """
                        def _recurse_expr_block(tree: Hint.SourceTree):
                            nonlocal expr
                            for node in tree.values():
                                expr += node['line'] + '\n'
                                _recurse_expr_block(node['children'])

                        _recurse_expr_block(node['children'])

                    x = out.setdefault(node['context']['self'], {})
                    x[prop] = (oper, expr)
Ejemplo n.º 7
0
 def target_file_exists(self, ofile: str):
     """
     FIXME:
         关于不同操作系统之间的困扰:
             对于 Windows 系统, QML 的文件对话框返回的是:
                 file:///d:/A/B/C
             我们去掉 'file:///', 就能判断文件已存在;
             对于 macOS 系统, QML 的文件对话框返回的是:
                 file:///Users/A/B/C
             如果去掉 'file:///', macOS 判断 'Users/A/B/C' 是不存在的,
             而 '/Users/A/B/C' 才是存在的.
             换句话说, 前者要求我们去掉 'file:///', 后者要求我们去掉 'file://', 该怎么
             统一操作呢?
     """
     lk.loga(ofile)
     return exists(ofile) and isfile(ofile)
Ejemplo n.º 8
0
def main(pyproj_file: str) -> TConf:
    """

    Args:
        pyproj_file: see template at `./template/pyproject.json`

    References:
        docs/pyproject-template.md
        docs/devnote/difference-between-roots.md > h2:pyproj_root
    """
    pyproj_root = pretty_path(ospath.dirname(pyproj_file))
    lk.loga(pyproj_root)

    conf = loads(pyproj_file)  # type: TConf
    conf = reformat_paths(conf, PathFormatter(pyproj_root))

    return conf
Ejemplo n.º 9
0
 def _register_method(self, method, name, narg):
     class_name = method.__qualname__.split('.')[-2]
     ''' e.g.
         class AAA:
             def mmm(self):
                 pass
                 
             class BBB:
                 def nnn(self):
                     pass
         
         print(AAA.mmm.__qualname__)  # -> 'AAA.mmm'
         print(BBB.nnn.__qualname__)  # -> 'AAA.BBB.nnn'
         
         don't use `class_name = method.__class__.__name__`, its value is
         always 'function'.
     '''
     method_name = method.__name__
     lk.loga(class_name, method_name)
     self._pyclass_holder[class_name][method_name] = (name, narg)
Ejemplo n.º 10
0
def main(file_i, file_o):
    """
    
    Args:
        file_i: '~/blueprint/resources/no2_all_qml_types.html'. 该文件被我事先从
            "{YourQtProgram}/Docs/Qt-{version}/qtdoc/qmltypes.html" 拷贝过来.
        file_o: 生成文件. "~/blueprint/resources/no3_all_qml_types.json"
            {module_group: {module: {type_name: path}, ...}, ...}
            #   {模组: {模块: {类型: 路径}}}
            e.g. {
                'qtquick': {
                    'qtquick': {
                        'Rectangle': 'qtquick/qml-qtquick-rectangle.html',
                        'Text': 'qtquick/qml-qtquick-text.html',
                        ...
                    },
                    'qtquick-window': {
                        'Window': 'qtquick/qml-qtquick-window-window.html',
                        ...
                    },
                    ...
                },
                ...
            }
    
    思路:
        1. 我们安装了 Qt 主程序以后, 在软件安装目录下的 'Docs/Qt-{version}' 中有
           它的 API 文档
        2. 其中 "~/Docs/Qt-{version}/qtdoc/qmltypes.html" 列出了全部的 qml types
        3. 我们对 "qmltypes.html" 用 BeautifulSoup 解析, 从中获取每个 qml types
           和它的链接, 最终我们将得到这些信息: 模组, 模块, 类型, 路径等
        4. 将这些信息保存到本项目下的 "~/resources/qmltypes.json" 文件中
    """
    soup = BeautifulSoup(read_and_write.read_file(file_i), 'html.parser')

    # https://www.itranslater.com/qa/details/2325827141935563776
    data = defaultdict(lambda: defaultdict(dict))
    #   {module_group: {module: {type_name: filename, ...}, ...}, ...}

    container = soup.find('div', 'flowListDiv')
    for e in container.find_all('dd'):
        link = e.a['href']  # type: str
        #   e.g. "../qtdatavisualization/qml-qtdatavisualization-
        #         abstract3dseries.html"

        match = re.search(r'\.\./(\w+)/([-\w]+)\.html', link)
        #                  |     ^-1-^ ^--2---^      |
        #                  ^-------- group(0) -------^
        #   match.group(0): '../qtdatavisualization/qml-qtdatavisualization
        #       -abstract3dseries.html'
        #   match.group(1): 'qtdatavisualization'
        #   match.group(2): 'qml-qtdatavisualization-abstract3dseries'
        assert match, e

        module_group = match.group(1)
        module = match.group(2)
        # see `blueprint/qml_modules_indexing/no1_all_qml_modules.py:comments
        # :针对 QtQuick Controls 的处理`
        if module_group == 'qtquickcontrols1':
            continue
        if 'qtquick-controls2' in module:
            #   e.g. 'qml-qtquick-controls2-label'
            module = module.replace('controls2', 'controls')

        path = match.group(0).lstrip('../')
        #   -> 'qtdatavisualization/qml-qtdatavisualization-abstract3dseries
        #   .html'
        module_group = _correct_module_lettercase(module_group)
        #   'qtdatavisualization' -> 'QtDataVisualization'
        module = _correct_module_lettercase('-'.join(module.split('-')[1:-1]))
        #   eg1: 'qml-qtdatavisualization-abstract3dseries' -> ['qml',
        #   'qtdatavisualization', 'abstract3dseries'] -> [
        #   'qtdatavisualization'] -> 'qtdatavisualization'
        #   -> 'QtDataVisualization'
        #   eg2: 'qml-qt3d-input-abstractactioninput' -> ['qml', 'qt3d',
        #   'input', 'abstractactioninput'] -> ['qt3d', 'input',
        #   'abstractactioninput'] -> 'qt3d-input' -> 'Qt3D.Input'
        #   注: 为什么要舍去末尾的元素? 因为末尾的那个是 `type_name`, 不是
        #   `module`. 接下来我们会抽取 `type_name`.
        type_name = e.text.split(':', 1)[0]
        #   注意我们不使用 `correct_module_lettercase(match.group(2).split('-')
        #   [-1])`, 是因为 `correct_module_lettercase` 的词库范围比较小, 仅对
        #   `module_group` 和 `module` 做了覆盖, 不能保证对 `type_name` 的处理正
        #   确; 而 `soup` 是可以比较轻松地通过 tag 提取到它的, 所以通过 html 元
        #   素获取.
        #   e.g. 'RadioButton: QtQuickControls' -> 'RadioButton'

        lk.loga(module_group, module, type_name)
        data[module_group][module][type_name] = path

    read_and_write.dumps(data, file_o)