Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Commit

Permalink
abi-gen: mv Py Handlebars helpers to own module
Browse files Browse the repository at this point in the history
Move all existing Python-related Handlebars helpers to the newly created
python_handlebars_helpers module.
  • Loading branch information
feuGeneA committed Nov 15, 2019
1 parent 58270e1 commit 2de9637
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 180 deletions.
162 changes: 2 additions & 160 deletions packages/abi-gen/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,14 @@ import chalk from 'chalk';
import * as changeCase from 'change-case';
import { execSync } from 'child_process';
import * as cliFormat from 'cli-format';
import {
AbiDefinition,
ConstructorAbi,
ContractAbi,
DataItem,
DevdocOutput,
EventAbi,
MethodAbi,
} from 'ethereum-types';
import { AbiDefinition, ConstructorAbi, ContractAbi, DevdocOutput, EventAbi, MethodAbi } from 'ethereum-types';
import { sync as globSync } from 'glob';
import * as Handlebars from 'handlebars';
import * as _ from 'lodash';
import * as mkdirp from 'mkdirp';
import toposort = require('toposort');
import * as yargs from 'yargs';

import { pythonHandlebarsHelpers } from './python_handlebars_helpers';
import { registerPythonHelpers } from './python_handlebars_helpers';
import { ContextData, ContractsBackend, ParamKind } from './types';
import { utils } from './utils';

Expand Down Expand Up @@ -147,155 +138,6 @@ function registerTypeScriptHelpers(): void {
);
}

function registerPythonHelpers(): void {
Handlebars.registerHelper('equal', (lhs: any, rhs: any) => {
return lhs === rhs;
});
Handlebars.registerHelper('safeString', (str: string) => new Handlebars.SafeString(str));
Handlebars.registerHelper('parameterType', utils.solTypeToPyType.bind(utils));
Handlebars.registerHelper('returnType', utils.solTypeToPyType.bind(utils));
Handlebars.registerHelper('toPythonIdentifier', utils.toPythonIdentifier.bind(utils));
Handlebars.registerHelper('sanitizeDevdocDetails', (_methodName: string, devdocDetails: string, indent: number) => {
// wrap to 80 columns, assuming given indent, so that generated
// docstrings can pass pycodestyle checks. also, replace repeated
// spaces, likely caused by leading indents in the Solidity, because
// they cause repeated spaces in the output, and in particular they may
// cause repeated spaces at the beginning of a line in the docstring,
// which leads to "unexpected indent" errors when generating
// documentation.
if (devdocDetails === undefined || devdocDetails.length === 0) {
return '';
}
const columnsPerRow = 80;
return new Handlebars.SafeString(
`\n${cliFormat.wrap(devdocDetails.replace(/ +/g, ' ') || '', {
paddingLeft: ' '.repeat(indent),
width: columnsPerRow,
ansi: false,
})}\n`,
);
});
Handlebars.registerHelper('makeParameterDocstringRole', (name: string, description: string, indent: number) => {
let docstring = `:param ${name}:`;
if (description && description.length > 0) {
docstring = `${docstring} ${description}`;
}
return new Handlebars.SafeString(utils.wrapPythonDocstringRole(docstring, indent));
});
Handlebars.registerHelper(
'makeReturnDocstringRole',
(description: string, indent: number) =>
new Handlebars.SafeString(
utils.wrapPythonDocstringRole(`:returns: ${description.replace(/ +/g, ' ')}`, indent),
),
);
Handlebars.registerHelper(
'makeEventParameterDocstringRole',
(eventName: string, indent: number) =>
new Handlebars.SafeString(
utils.wrapPythonDocstringRole(
`:param tx_hash: hash of transaction emitting ${eventName} event`,
indent,
),
),
);
Handlebars.registerHelper('tupleDefinitions', (abisJSON: string) => {
const abis: AbiDefinition[] = JSON.parse(abisJSON);
// build an array of objects, each of which has one key, the Python
// name of a tuple, with a string value holding the body of a Python
// class representing that tuple. Using a key-value object conveniently
// filters duplicate references to the same tuple.
const tupleBodies: { [pythonTupleName: string]: string } = {};
// build an array of tuple dependencies, whose format conforms to the
// expected input to toposort, a function to do a topological sort,
// which will help us declare tuples in the proper order, avoiding
// references to tuples that haven't been declared yet.
const tupleDependencies: Array<[string, string]> = [];
for (const abi of abis) {
let parameters: DataItem[] = [];
if (abi.hasOwnProperty('inputs')) {
// HACK(feuGeneA): using "as MethodAbi" below, but abi
// could just as well be ConstructorAbi, EventAbi, etc. We
// just need to tell the TypeScript compiler that it's NOT
// FallbackAbi, or else it would complain, "Property
// 'inputs' does not exist on type 'AbiDefinition'.
// Property 'inputs' does not exist on type
// 'FallbackAbi'.", despite the enclosing if statement.
// tslint:disable:no-unnecessary-type-assertion
parameters = parameters.concat((abi as MethodAbi).inputs);
}
if (abi.hasOwnProperty('outputs')) {
// HACK(feuGeneA): same as described above, except here we
// KNOW that it's a MethodAbi, given the enclosing if
// statement, because that's the only AbiDefinition subtype
// that actually has an outputs field.
parameters = parameters.concat((abi as MethodAbi).outputs);
}
for (const parameter of parameters) {
utils.extractTuples(parameter, tupleBodies, tupleDependencies);
}
}
// build up a list of tuples to declare. the order they're pushed into
// this array is the order they will be declared.
const tuplesToDeclare = [];
// first push the ones that have dependencies
tuplesToDeclare.push(...toposort(tupleDependencies));
// then push any remaining bodies (the ones that DON'T have
// dependencies)
for (const pythonTupleName in tupleBodies) {
if (!tuplesToDeclare.includes(pythonTupleName)) {
tuplesToDeclare.push(pythonTupleName);
}
}
// now iterate over those ordered tuples-to-declare, and prefix the
// corresponding class bodies with their class headers, to form full
// class declarations.
const tupleDeclarations = [];
for (const pythonTupleName of tuplesToDeclare) {
if (tupleBodies[pythonTupleName]) {
tupleDeclarations.push(
`class ${pythonTupleName}(TypedDict):\n """Python representation of a tuple or struct.\n\n Solidity compiler output does not include the names of structs that appear\n in method definitions. A tuple found in an ABI may have been written in\n Solidity as a literal, anonymous tuple, or it may have been written as a\n named \`struct\`:code:, but there is no way to tell from the compiler\n output. This class represents a tuple that appeared in a method\n definition. Its name is derived from a hash of that tuple's field names,\n and every method whose ABI refers to a tuple with that same list of field\n names will have a generated wrapper method that refers to this class.\n\n Any members of type \`bytes\`:code: should be encoded as UTF-8, which can be\n accomplished via \`str.encode("utf_8")\`:code:\n """${
tupleBodies[pythonTupleName]
}`,
);
}
}
// finally, join the class declarations together for the output file
return new Handlebars.SafeString(tupleDeclarations.join('\n\n\n'));
});
Handlebars.registerHelper('docBytesIfNecessary', (abisJSON: string) => {
const abis: AbiDefinition[] = JSON.parse(abisJSON);
// see if any ABIs accept params of type bytes, and if so then emit
// explanatory documentation string.
for (const abi of abis) {
if (abi.hasOwnProperty('inputs')) {
// HACK(feuGeneA): using "as MethodAbi" below, but abi
// could just as well be ConstructorAbi, EventAbi, etc. We
// just need to tell the TypeScript compiler that it's NOT
// FallbackAbi, or else it would complain, "Property
// 'inputs' does not exist on type 'AbiDefinition'.
// Property 'inputs' does not exist on type
// 'FallbackAbi'.", despite the enclosing if statement.
// tslint:disable:no-unnecessary-type-assertion
if ((abi as MethodAbi).inputs) {
for (const input of (abi as MethodAbi).inputs) {
if (input.type === 'bytes') {
return new Handlebars.SafeString(
'\n\n All method parameters of type `bytes`:code: should be encoded as UTF-8,\n which can be accomplished via `str.encode("utf_8")`:code:.\n ',
);
}
}
}
}
}
return '';
});
Handlebars.registerHelper(
'toPythonClassname',
(sourceName: string) => new Handlebars.SafeString(changeCase.pascal(sourceName)),
);
Handlebars.registerHelper('makeOutputsValue', pythonHandlebarsHelpers.makeOutputsValue);
}
if (args.language === 'TypeScript') {
registerTypeScriptHelpers();
} else if (args.language === 'Python') {
Expand Down
Loading

0 comments on commit 2de9637

Please sign in to comment.