@ -1,7 +1,9 @@
# vim: fileencoding=utf-8
#
import os
import importlib
import importlib . util
from pprint import pprint
from jinja2 import Environment , FileSystemLoader
from calculate . variables . datavars import NamespaceNode , VariableNode , \
ListType , IntegerType , \
FloatType , IniType , TableType , \
@ -9,6 +11,8 @@ from calculate.variables.datavars import NamespaceNode, VariableNode,\
VariableNotFoundError
from calculate . utils . gentoo import ProfileWalker
from calculate . utils . files import read_file , FilesError
from calculate . utils . tools import Singleton
from calculate . utils . io_module import IOModule
from pyparsing import Literal , Word , ZeroOrMore , Group , Optional , restOfLine , \
empty , printables , OneOrMore , lineno , line , col , SkipTo , \
LineEnd , Combine , nums
@ -16,13 +20,19 @@ from enum import Enum
from contextlib import contextmanager
class LoaderError ( Exception ) :
''' Исключение выбрасываемое загрузчиком, если тот в принципе не может
загрузить переменные . '''
pass
class Define ( Enum ) :
assign = 0
append = 1
remove = 2
class CalculateIniParser :
class CalculateIniParser (metaclass = Singleton ) :
''' Класс парсера calculate.ini файлов. '''
def __init__ ( self ) :
@ -140,11 +150,16 @@ class NamespaceIniFiller:
из calculate . ini файла . '''
available_sections = { ' custom ' }
def __init__ ( self , restrict_creation = True ):
def __init__ ( self , restrict_creation = True , ouput = None ):
self . ini_parser = CalculateIniParser ( )
# Флаги, определяющие возможность создания новых переменных и новых
# пространств имен в данном пространстве имен.
self . restricted = restrict_creation
self . modify_only = False
self . output = ouput
def error ( self , lineno , error_message ) :
self . errors . append ( lineno , error_message )
@ -184,12 +199,14 @@ class NamespaceIniFiller:
''' Метод для получения доступа и создания пространств имен. '''
if self . restricted :
self . modify_only = sections [ 0 ] not in self . available_sections
self . current_namespace = self . namespace
for section in sections :
if isinstance ( self . current_namespace , Datavars ) :
if section not in self . current_namespace :
raise VariableError ( " variables package ' {} ' is not found " .
format ( section ) )
# TODO Поменять на простой вывод.
raise VariableNotFoundError ( " variables package ' {} ' is not "
" found " . format ( section ) )
elif isinstance ( self . current_namespace , NamespaceNode ) :
if section not in self . current_namespace . namespaces :
if ( section in self . current_namespace . variables and
@ -206,6 +223,7 @@ class NamespaceIniFiller:
else :
self . current_namespace = None
return
self . current_namespace = self . current_namespace . namespaces [ section ]
def clear_section ( self , sections : list ) - > None :
@ -266,6 +284,7 @@ class NamespaceIniFiller:
table_variable . source = table
def define_key ( self , key : str , value : str , optype ) - > None :
''' Метод для создания и модификации переменных. '''
if self . current_namespace is None :
return
@ -301,7 +320,10 @@ class NamespaceIniFiller:
source = value )
else :
# TODO Какая-то обработка ошибки.
pass
self . output . set_error ( " Can not create variable ' {} . {} ' in not "
" ' custom ' namespace " . format (
self . current_namespace . get_fullname ( ) ,
key ) )
def append_value ( self , key : str , value : str ) - > None :
''' Метод выполняющий действия возложенные на оператор +=. '''
@ -405,6 +427,7 @@ class VariableLoader:
def __init__ ( self , datavars , variables_path , repository_map = None ) :
self . datavars = datavars
self . output = datavars . output
self . ini_filler = NamespaceIniFiller ( )
self . variables_path = variables_path
@ -432,8 +455,9 @@ class VariableLoader:
if section in current_namespace :
current_namespace = current_namespace [ section ]
else :
# TODO детальнее продумать действия при отсутствии нужной
# переменной.
self . output . set_error ( " Variable ' os.gentoo.repositories ' "
" is not found. Can not load profile "
" variables. " )
return
self . repository_map = self . _get_repository_map ( self . datavars )
@ -442,8 +466,9 @@ class VariableLoader:
' path ' in self . datavars . os . gentoo . profile ) :
profile_path = self . datavars . os . gentoo . profile . path
else :
# TODO детальнее продумать действия при отсутствии нужной
# переменной.
self . output . set_error ( " Variable ' os.gentoo.profile.path ' "
" is not found. Can not load profile "
" variables. " )
return
self . _fill_from_ini ( profile_path )
@ -451,13 +476,17 @@ class VariableLoader:
def load_user_variables ( self ) :
''' Метод для загрузки переменных из calculate.ini указанных в
переменных env_order и env_path . '''
if ( ' system ' in self . datavars and ' env_order ' in self . datavars . system
and ' env_path ' in self . datavars . system ) :
try :
env_order = self . datavars . system . env_order
env_path = self . datavars . system . env_path
for ini_file in env_order :
if ini_file in env_path :
self . fill_from_custom_ini ( env_path [ ini_file ] . value )
except VariableNotFoundError as error :
self . output . set_warning ( " Can not load additional variables: {} " .
format ( str ( error ) ) )
return
for ini_file in env_order :
if ini_file in env_path :
self . fill_from_custom_ini ( env_path [ ini_file ] . value )
def _fill_from_package ( self , current_namespace : NamespaceNode ,
directory_path : str , package : str ) - > None :
@ -500,8 +529,8 @@ class VariableLoader:
ini_file_text = read_file ( file_path )
self . ini_filler . fill ( self . datavars , ini_file_text )
except FilesError :
# TODO продумать обработку ошибок.
pass
self . output . set_error ( " Can not load profile variables from "
" unexisting file: {} " . format ( file_path ) )
def _get_repository_map ( self , datavars ) :
''' Метод для получения из переменной словаря с репозиториями и путями
@ -517,6 +546,7 @@ class VariableLoader:
@contextmanager
def test ( self , file_name , namespace ) :
''' Контекстный менеджер для тестирования. '''
print ( ' IMPORT: {} . {} ' . format ( namespace . get_fullname ( ) , file_name ) )
try :
yield self
@ -526,16 +556,73 @@ class VariableLoader:
class CalculateIniSaver :
''' Класс для сохранения значений п ользовательских п еременных.'''
def __init__ ( self ):
''' Класс для сохранения значений п еременных в указанные ini-файлы .'''
def __init__ ( self , ini_parser = None ):
self . ini_parser = CalculateIniParser ( )
self . operations = { Define . assign : ' = ' ,
Define . append : ' += ' ,
Define . remove : ' -= ' }
file_loader = FileSystemLoader ( ' calculate/variables ' )
environment = Environment ( loader = file_loader )
self . ini_template = environment . get_template ( ' ini_template ' )
def save_to_ini ( self , target_path , variables_to_save ) :
''' Метод для сохранения переменных в указанный ini-файл. '''
ini_file_text = read_file ( target_path )
ini_dictionary = self . _parse_ini ( ini_file_text )
for namespace in variables_to_save :
if namespace in ini_dictionary :
ini_dictionary [ namespace ] . update ( variables_to_save [ namespace ] )
else :
ini_dictionary [ namespace ] = variables_to_save [ namespace ]
ini_file_text = self . _get_ini_text ( ini_dictionary )
with open ( target_path , ' w ' ) as ini_file :
ini_file . write ( ini_file_text )
def _parse_ini ( self , ini_file_text ) :
''' Метод для разбора текста ini-файла в словарь, в который далее будут
добавляться измененные переменные . '''
current_namespace = None
ini_dictionary = dict ( )
for parsed_line in self . ini_parser . parse ( ini_file_text ) :
line_type = next ( iter ( parsed_line ) )
line_content = parsed_line [ line_type ]
if ( line_type == ' start_section ' or
line_type == ' start_table ' ) :
current_namespace = tuple ( line_content [ 0 ] )
if current_namespace not in ini_dictionary :
ini_dictionary [ current_namespace ] = dict ( )
elif ( line_type == ' clear_section ' or
line_type == ' clear_table ' ) :
current_namespace = ( * line_content [ 0 ] , ' ' )
ini_dictionary [ current_namespace ] = dict ( )
elif line_type == ' define_key ' :
namespace = ini_dictionary [ current_namespace ]
namespace . update ( { line_content [ 0 ] :
( self . operations [ line_content [ 2 ] ] ,
line_content [ 1 ] ) } )
return ini_dictionary
def _get_ini_text ( self , ini_dictionary ) :
''' Метод для получения текста ini файла, полученного в результате
наложения изменений из тегов save в шаблонах . '''
ini_text = self . ini_template . render ( ini_dictionary = ini_dictionary )
return ini_text . strip ( )
class Datavars :
''' Класс для хранения переменных и управления ими. '''
def __init__ ( self , variables_path = ' calculate/vars ' , repository_map = None ) :
def __init__ ( self , variables_path = ' calculate/vars ' , repository_map = None ,
io_module = IOModule ( ) ) :
self . _variables_path = variables_path
self . _available_packages = self . _get_available_packages ( )
self . output = io_module
self . root = NamespaceNode ( ' <root> ' )
self . _loader = VariableLoader ( self , self . _variables_path ,
@ -619,59 +706,20 @@ class Datavars:
self . _loader . load_variables_package ( package_name )
return True
def add_namespace ( self , namespace_node ) :
self . root . add_namespace ( namespace_node )
@property
def namespaces ( self ) :
return self . root . namespaces
def save_variables ( self ) :
''' Метод для сохранения в calculate.ini файлах '''
ini_parser = self . _loader . ini_filler . ini_parser
''' Метод для сохранения значений переменных в calculate.ini файлах. '''
target_paths = self . system . env_path
operations = { Define . assign : ' = ' ,
Define . append : ' += ' ,
Define . remove : ' -= ' }
saver = CalculateIniSaver ( )
for target in self . variables_to_save :
if self . variables_to_save [ target ] :
dict_to_save = self . variables_to_save [ target ]
# Распарсим файл в словарь.
current_namespace = None
ini_dictionary = dict ( )
ini_file_text = read_file ( target_paths [ target ] . value )
for parsed_line in ini_parser . parse ( ini_file_text ) :
line_type = next ( iter ( parsed_line ) )
print ( ' line_type: {} ' . format ( line_type ) )
line_content = parsed_line [ line_type ]
print ( ' line_content: {} ' . format ( line_content ) )
if ( line_type == ' start_section ' or
line_type == ' start_table ' ) :
current_namespace = tuple ( line_content [ 0 ] )
if current_namespace not in ini_dictionary :
ini_dictionary [ current_namespace ] = dict ( )
elif ( line_type == ' clear_section ' or
line_type == ' clear_table ' ) :
current_namespace = ( * line_content [ 0 ] , ' ' )
ini_dictionary [ current_namespace ] = dict ( )
elif line_type == ' define_key ' :
namespace = ini_dictionary [ current_namespace ]
namespace . update ( { line_content [ 0 ] :
( operations [ line_content [ 2 ] ] ,
line_content [ 1 ] ) } )
for namespace in dict_to_save :
if namespace in ini_dictionary :
ini_dictionary [ namespace ] . update (
dict_to_save [ namespace ] )
else :
ini_dictionary [ namespace ] = dict_to_save [ namespace ]
print ( ' INI DICTIONARY FOR {} : ' . format ( target ) )
pprint ( ini_dictionary )
def _get_text ( self , ini_dictionary ) :
pass
target_path = target_paths [ target ] . value
saver . save_to_ini ( target_path , dict_to_save )