import os
import shutil
import inspect
import pkg_resources
from grpc_tools.protoc import main as protoc
POSIX_SEP = '/'
def paths_print(items):
print("Paths:")
print("-"*80)
for k, v in items:
print("\t", k, "\t\t", v)
print("-"*80)
def clear_folder(output_dir):
if os.path.exists(output_dir):
shutil.rmtree(output_dir)
def generate_bindings(protos_dir, axxonsoft_protos_dir):
proto_files_relative = get_proto_files_relpath(axxonsoft_protos_dir, protos_dir)
protoc_keys = [
f'-I{protos_dir}',
'--python_out=.',
'--grpc_python_out=.',
]
protoc_args = protoc_keys + proto_files_relative
is_protoc_patched = 'include_module_proto' in inspect.getfullargspec(protoc).args
if not is_protoc_patched:
protoc_args_patch_start = [inspect.getfile(protoc)]
protoc_args_patch_end = [f'-I{pkg_resources.resource_filename("grpc_tools", "_proto")}']
else:
protoc_args_patch_start = protoc_args_patch_end = []
print('Call of "protoc":')
protoc_retcode = protoc(protoc_args_patch_start + protoc_args + protoc_args_patch_end)
# print(f'\targs = {protoc_args}\n\tretcode = {protoc_retcode}\n')
return protoc_retcode
def generate_init_py_files(bindings_output_dir):
def make_init_py_subtree(base_path):
# print('\tprocess {!r}'.format(base_path))
make_init_py(base_path)
for subdir in get_subdirs(base_path):
make_init_py_subtree(subdir)
def make_init_py(base_path):
modules = get_py_modules(base_path)
init_py_path = os.path.join(base_path, '__init__.py')
with open(init_py_path, 'w') as init_py:
init_py.write('# Generated AUTOMATICALLY by \"{}\".\n'.format(os.path.basename(__file__)))
init_py.write('# DO NOT EDIT manually!\n\n')
for m in modules:
if '.Internal' not in m:
init_py.write('from . import {!s}\n'.format(m))
def get_subdirs(base_path):
_, subdirs, _ = next(os.walk(base_path))
return [os.path.abspath(os.path.join(base_path, s)) for s in subdirs]
def get_py_modules(base_path):
_, subdirs, files = next(os.walk(base_path))
file_modules = [f.rstrip('.py') for f in files if f.endswith('.py') and f != '__init__.py']
return subdirs + file_modules
# print('Generate "__init__.py" files:')
make_init_py_subtree(bindings_output_dir)
def get_proto_files_relpath(axxonsoft_protos_dir, protos_dir):
out = []
for root, dirs, files in os.walk(axxonsoft_protos_dir):
for file in files:
if file.endswith(".proto") and ".Internal" not in file:
full_path = os.path.abspath(os.path.join(root, file))
rel_path = os.path.relpath(full_path, protos_dir)
posix_rel_path = rel_path.replace(os.sep, POSIX_SEP)
out.append(posix_rel_path)
return out
def run_generate():
protos_dir_name = 'grpc-proto-files'
axxonsoft_dir_name = 'axxonsoft'
current_dir = os.path.dirname(os.path.abspath(__file__))
protos_dir = os.path.join(current_dir, protos_dir_name)
axxonsoft_protos_dir = os.path.join(protos_dir, axxonsoft_dir_name)
bindings_package_dir = os.path.join(current_dir, axxonsoft_dir_name)
paths_print([
('protos_dir', protos_dir),
('axxonsoft_protos_dir', axxonsoft_protos_dir),
('bindings_package_dir', bindings_package_dir),
])
clear_folder(bindings_package_dir)
code = generate_bindings(protos_dir, axxonsoft_protos_dir)
if code == 0:
generate_init_py_files(bindings_package_dir)
return code
if __name__ == '__main__':
print('AxxonNext NativeBL bindings generator.')
print('To generate that bindings you need to copy to `grpc-proto-files` folder: ')
print('1) `axxonsoft` folder with AxxonSoft proto-files, ')
print('2) `google` folder with Google common proto-files.')
result = run_generate()
if result == 0:
print('Bindings generation was completed successfully')
else:
print(f'An error occurred while generating bindings: {result}')
Create the protoc folder in the script folder. Place the axxonsoft and google folders in this folder along with their contents from the resulting archive with proto files.
Run the script.
Authorization and first request
To send requests through the gRPC channel, the authorization in NativeBL is required. To do this, use the Server certificate from the C:\ProgramData\AxxonSoft\AxxonNext\Tickets folder.
You can be authorized only to the Server from the certificate.
For authorization, use the get_authorized_channel method, in which it is necessary to specify the following: