Use data classes instead of namedtuples (#508)

* Build mypy files for protos in tox env

* Include mypy check in make test

* Improve building offer from remote peer download

* Remove old methods from peer task
This commit is contained in:
Jonathan Zernik 2020-12-26 01:00:58 -08:00 committed by GitHub
parent 462626035c
commit 0a5a54554c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 102 additions and 97 deletions

1
.gitignore vendored
View file

@ -18,5 +18,6 @@ dist/
# Ignore generated python proto files
*pb2.py
*pb2_grpc.py
*.pyi
libs/

View file

@ -7,11 +7,13 @@ clean:
find . -name '*~' -delete
find . -name '*pb2_grpc.py' -delete
find . -name '*pb2.py' -delete
find . -name '*.pyi' -delete
make --directory=frontend clean;
test:
tox
tox -e codechecks
tox -e mypy
codeformat:
tox -e autoflake

View file

@ -4,6 +4,7 @@ argparse
googleapis-common-protos
grpcio
grpcio-tools
mypy-protobuf
psycopg2
requests
SQLAlchemy

View file

@ -1,11 +1,16 @@
import io
import os
import pkg_resources
import sys
import setuptools.command.build_py
import setuptools.command.test
# from grpc_tools.command import BuildPackageProtos
from setuptools import find_packages, setup
from setuptools import Command
from grpc_tools import protoc
PACKAGE_DIRECTORIES = {
'': '.',
}
@ -22,13 +27,32 @@ class BuildPyCommand(setuptools.command.build_py.build_py):
setuptools.command.build_py.build_py.run(self)
# class TestCommand(setuptools.command.test.test):
# """Custom test command."""
def build_package_protos(package_root, strict_mode=False):
proto_files = []
inclusion_root = os.path.abspath(package_root)
for root, _, files in os.walk(inclusion_root):
for filename in files:
if filename.endswith('.proto'):
proto_files.append(os.path.abspath(os.path.join(root,
filename)))
# def run(self):
# print("Running custom test command...")
# self.run_command('build_proto_modules')
# # setuptools.command.test.test.run(self)
well_known_protos_include = pkg_resources.resource_filename(
'grpc_tools', '_proto')
for proto_file in proto_files:
command = [
'grpc_tools.protoc',
'--proto_path={}'.format(inclusion_root),
'--proto_path={}'.format(well_known_protos_include),
'--python_out={}'.format(inclusion_root),
'--grpc_python_out={}'.format(inclusion_root),
'--mypy_out={}'.format(inclusion_root),
] + [proto_file]
if protoc.main(command) != 0:
if strict_mode:
raise Exception('error: {} failed'.format(command))
else:
sys.stderr.write('warning: {} failed'.format(command))
class BuildPackageProtos(Command):
@ -44,8 +68,9 @@ class BuildPackageProtos(Command):
pass
def run(self):
import grpc_tools.command
grpc_tools.command.build_package_protos('.')
# import grpc_tools.command
# grpc_tools.command.build_package_protos('.')
build_package_protos('.')
setup(
name="squeaknode",
@ -66,6 +91,5 @@ setup(
cmdclass={
'build_proto_modules': BuildPackageProtos,
'build_py': BuildPyCommand,
# 'test': TestCommand,
},
)

View file

@ -1,20 +1,21 @@
from collections import namedtuple
from dataclasses import dataclass
Offer = namedtuple(
"Offer",
[
"offer_id",
"squeak_hash",
"price_msat",
"payment_hash",
"nonce",
"payment_point",
"invoice_timestamp",
"invoice_expiry",
"payment_request",
"destination",
"node_host",
"node_port",
"peer_id",
],
)
from typing import Optional
@dataclass
class Offer:
"""Class for saving an offer from a remote peer."""
offer_id: Optional[int]
squeak_hash: str
price_msat: bytes
payment_hash: str
nonce: str
payment_point: str
invoice_timestamp: int
invoice_expiry: int
payment_request: str
destination: str
node_host: str
node_port: int
peer_id: int

View file

@ -149,7 +149,45 @@ class PeerSyncTask:
squeak = self._get_local_squeak(squeak_hash)
# Download the buy offer
offer = self._download_buy_offer(squeak_hash)
offer_msg = self._download_offer_msg(squeak_hash)
# Decode the payment request
pay_req = self._decode_payment_request(offer_msg.payment_request)
logger.info("Decoded payment request: {}".format(pay_req))
# TODO: Use the real payment point, not a fake value.
squeak_payment_point = squeak.paymentPoint
payment_point = b''
payment_hash = bytes.fromhex(pay_req.payment_hash)
price_msat = pay_req.num_msat
destination = pay_req.destination
invoice_timestamp = pay_req.timestamp
invoice_expiry = pay_req.expiry
node_host = offer_msg.host or self.peer.host
node_port = offer_msg.port
logger.info("price_msat: {}".format(price_msat))
logger.info("destination: {}".format(destination))
logger.info("invoice_timestamp: {}".format(invoice_timestamp))
logger.info("invoice_expiry: {}".format(invoice_expiry))
logger.info("node_host: {}".format(node_host))
logger.info("node_port: {}".format(node_port))
decoded_offer = Offer(
offer_id=None,
squeak_hash=offer_msg.squeak_hash,
price_msat=price_msat,
payment_hash=payment_hash,
nonce=offer_msg.nonce,
payment_point=squeak_payment_point,
invoice_timestamp=invoice_timestamp,
invoice_expiry=invoice_expiry,
payment_request=offer_msg.payment_request,
destination=destination,
node_host=node_host,
node_port=node_port,
peer_id=self.peer.peer_id,
)
# TODO: Check the payment point
# payment_point = offer.payment_point
@ -164,9 +202,6 @@ class PeerSyncTask:
# )
# )
# Get the decoded offer from the payment request string
decoded_offer = self._get_decoded_offer(offer)
# Save the offer
self._save_offer(decoded_offer)
@ -232,75 +267,15 @@ class PeerSyncTask:
sharing_profiles = self.squeak_db.get_sharing_profiles()
return [profile.address for profile in sharing_profiles]
def _download_buy_offer(self, squeak_hash):
def _download_offer_msg(self, squeak_hash):
logger.info(
"Downloading buy offer for squeak hash: {}".format(squeak_hash)
)
offer_msg = self.peer_client.buy_squeak(squeak_hash)
offer = self._offer_from_msg(offer_msg)
return offer
return self.peer_client.buy_squeak(squeak_hash)
def _save_offer(self, offer):
logger.info("Saving offer: {}".format(offer))
self.squeak_db.insert_offer(offer)
def _offer_from_msg(self, offer_msg):
if not offer_msg:
return None
return Offer(
offer_id=None,
squeak_hash=offer_msg.squeak_hash,
price_msat=None,
payment_hash=None,
nonce=offer_msg.nonce,
payment_point=None,
invoice_timestamp=None,
invoice_expiry=None,
payment_request=offer_msg.payment_request,
destination=None,
node_host=offer_msg.host,
node_port=offer_msg.port,
peer_id=self.peer.peer_id,
)
def _decode_payment_request(self, payment_request):
return self.lightning_client.decode_pay_req(payment_request)
def _get_decoded_offer(self, offer):
pay_req = self._decode_payment_request(offer.payment_request)
logger.info("Decoded payment request: {}".format(pay_req))
# TODO: Use the real payment point, not a fake value.
payment_point = b''
payment_hash = bytes.fromhex(pay_req.payment_hash)
price_msat = pay_req.num_msat
destination = pay_req.destination
invoice_timestamp = pay_req.timestamp
invoice_expiry = pay_req.expiry
node_host = offer.node_host or self.peer.host
node_port = offer.node_port
logger.info("price_msat: {}".format(price_msat))
logger.info("destination: {}".format(destination))
logger.info("invoice_timestamp: {}".format(invoice_timestamp))
logger.info("invoice_expiry: {}".format(invoice_expiry))
logger.info("node_host: {}".format(node_host))
logger.info("node_port: {}".format(node_port))
decoded_offer = Offer(
offer_id=offer.offer_id,
squeak_hash=offer.squeak_hash,
price_msat=price_msat,
payment_hash=payment_hash,
nonce=offer.nonce,
payment_point=payment_point,
invoice_timestamp=invoice_timestamp,
invoice_expiry=invoice_expiry,
payment_request=offer.payment_request,
destination=destination,
node_host=node_host,
node_port=node_port,
peer_id=offer.peer_id,
)
return decoded_offer

View file

@ -9,6 +9,7 @@ deps =
-rrequirements.txt
passenv = MY_APP_CONFIG_FILE
commands =
{envbindir}/python setup.py build_proto_modules
py.test {posargs:tests}
[testenv:coverage]
@ -40,8 +41,8 @@ codechecks_paths =
tests
itests
commands =
flake8
reorder-python-imports
flake8 {posargs:{[testenv:codechecks]codechecks_paths}}
reorder-python-imports {posargs:{[testenv:codechecks]codechecks_paths}}
[testenv:black]
basepython = python3.8