Module: DataMetaDom::PythonLexer

Includes:
DataMetaDom
Defined in:
lib/dataMetaDom/python.rb

Overview

Definition for generating Python basic classes that do not relate to serialization layer.

For command line details either check the new method’s source or the README.rdoc file, the usage section.

Defined Under Namespace

Classes: PyRegExRoster

Constant Summary collapse

INIT_PY_HEADER =

Standard header for every __init__.py file

%q<
# see https://docs.python.org/3/library/pkgutil.html
# without this, Python will have trouble finding packages that share some common tree off the root
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

>
MODEL_HEADER =

The top of each model file.

%q|# This file is generated by DataMeta DOM. Do not edit manually!
# package %s

import re

from ebay_datameta_core.base import Verifiable, DateTime, Migrator, SemVer
from ebay_datameta_core.canned_re import CannedRe

# noinspection PyCompatibility
from enum import Enum

|

Constants included from DataMetaDom

BITSET, BOOL, BOOL_CONV, CANNED_RX, CHAR, CONVS, DATAMETA_LIB, DATETIME, DIMMED_TYPES, DOC, DOC_TARGETS, DTTM_CONV, DTTM_TYPE, END_KW, ENUM, FLOAT, FLOAT4, FLOAT8, FRACT_CONV, FULL_COMPARE, IDENTITY, ID_ONLY_COMPARE, ID_START, INCLUDE, INDENT, INDEX, INT, INT1, INT2, INT4, INT8, INTEGRAL_CONV, JAVA_DOC_TARGET, L, MAPPING, MATCHES, MODEL_LEVEL_TOKENS, NAMESPACE, NO_NAMESPACE, NUMERIC, OPTIONAL_PFX, OPT_DIMMABLE, PACK_SEPARATOR, PLAIN_DOC_TARGET, RAW, RECORD, RECORD_LEVEL_TOKENS, REC_ATTR_KEYWORDS, REQUIRED_PFX, SAME_FULL_SFX, SAME_ID_SFX, SCALE_TYPES, SOURCE_INDENT, STANDARD_TYPES, STRING, TEXT_CONV, TYPE_START, UNIQUE, URL, URL_TYPE, VERSION, VER_KW, WIKI, WIKI_REF_HTML

Instance Method Summary collapse

Methods included from DataMetaDom

combineNsBase, condenseType, fullTypeName, getParenDimInfo, getterName, help, helpAndVerFirstArg, helpMySqlDdl, #helpOracleDdl, helpPojoGen, helpScalaGen, #migrClass, nsAdjustment, #qualName, setterName, splitNameSpace, uniPath, validNs?

Instance Method Details

#genPy(model, outRoot) ⇒ Object

Generates Python sources for the model, the “plain” Python part, without fancy dependencies.

  • Parameters

    • parser - instance of Model

    • outRoot - output directory



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/dataMetaDom/python.rb', line 84

def genPy(model, outRoot)
    firstRecord = model.records.values.first
    @pyPackage, baseName, packagePath = DataMetaDom::PojoLexer::assertNamespace(firstRecord.name)

    # Next: replace dots with underscores.
    # The path also adjusted accordingly.
    #
    # Rationale for this, quoting PEP 8:
    #
    #    Package and Module Names
    #
    #    Modules should have short, all-lowercase names. Underscores can be used in the module name if it improves
    #    readability. Python packages should also have short, all-lowercase names, although the use of underscores
    #    is discouraged.
    #
    # Short and all-lowercase names, and improving readability if you have complex system and need long package names,
    # is "discouraged". Can't do this here, our system is more complicated for strictly religous, "pythonic" Python.
    # A tool must be enabling, and in this case, this irrational ruling gets in the way.
    # And dots are a no-no, Python can't find packages with complicated package structures and imports.
    #
    # Hence, we opt for long package names with underscores for distinctiveness and readability:
    @pyPackage = @pyPackage.gsub('.', '_')
    packagePath = packagePath.gsub('/', '_')
    destDir = File.join(outRoot, packagePath)
    FileUtils.mkdir_p destDir
    # build the package path and create __init__.py files in each package's dir if it's not there.
    @destDir = packagePath.split(File::SEPARATOR).reduce(outRoot){ |s,v|
        r = s.empty? ? v : File.join(s, v) # next package dir
        FileUtils.mkdir_p r # create if not there
        # create the __init__.py with proper content if it's not there
        df = File.join(r, '__init__.py'); IO.write(df, INIT_PY_HEADER, mode: 'wb') unless r == outRoot || File.file?(df)
        r # pass r for the next reduce iteration
    }
    vars =  OpenStruct.new # for template's local variables. ERB does not make them visible to the binding
    modelFile = File.join(@destDir, 'model.py')
    [modelFile].each{|f| FileUtils.rm f if File.file?(f)}
    IO.write(modelFile, MODEL_HEADER % @pyPackage, mode: 'wb')
    (model.records.values + model.enums.values).each { |srcE| # it is important that the records render first
        _, baseName, _ = DataMetaDom::PojoLexer::assertNamespace(srcE.name)

        pyClassName = baseName

        case
            when srcE.kind_of?(DataMetaDom::Record)
                fields = srcE.fields
                rxRoster = PyRegExRoster.new
                eqHashFields = srcE.identity ? srcE.identity.args : fields.keys.sort
                reqFields = fields.values.select{|f| f.isRequired }.map{|f| f.name}
                verCalls = reqFields.map{|r| %<if(self.__#{r} is None): missingFields.append("#{r}");>}.join("\n#{INDENT * 2}")
                fieldVerifications = ''
                fields.each_key { |k|
                    f = fields[k]
                    dt = f.dataType
                    rxRoster.register(f) if f.regex
                    if f.trgType # Maps: if either the key or the value is verifiable, do it
                        mainVf = model.records[dt.type] # main data type is verifiable
                        trgVf = model.records[f.trgType.type]  # target type is verifiable
                        if mainVf || trgVf
                            fieldVerifications << "\n#{INDENT*2}#{!f.isRequired ? "if(self.__#{f.name} is not None):\n#{INDENT*3}" : '' }for k, v in self.__#{f.name}.iteritems():#{mainVf ? 'k.verify();' : ''} #{trgVf ? 'v.verify()' :''}\n"
                        end
                    end

                    if model.records[dt.type] && !f.trgType # maps handled separately
                        fieldVerifications << "\n#{INDENT*2}#{!f.isRequired ? "if(self.__#{f.name} is not None): " : '' }#{f.aggr ? "[v___#{f.name}.verify() for v___#{f.name} in self.__#{f.name} ]" : "self.__#{f.name}.verify()"}"
                        # the Verifiable::verify method reference works just fine, tested it: Java correctly calls the method on the object
                    end
                }
                IO::write(File.join(@destDir, 'model.py'),
                          ERB.new(IO.read(File.join(File.dirname(__FILE__), '../../tmpl/python/entity.erb')),
                                  $SAFE, '%<>').result(binding), mode: 'ab')
            when srcE.kind_of?(DataMetaDom::Mappings)
#                        FIXME -- implement!!!
            when srcE.kind_of?(DataMetaDom::Enum)
                IO.write(modelFile, %|#{baseName} = Enum("#{baseName}", "#{srcE.rawVals.join(' ')}")\n\n|, mode: 'ab')
#                        # handled below, bundled in one huge file
            when srcE.kind_of?(DataMetaDom::BitSet)
#                        FIXME -- implement!!!
            else
                raise "Unsupported Entity: #{srcE.inspect}"
        end
    }
end