Add custom fields to chain spec, harden custom field imports, improve dumpconfig handle
This commit is contained in:
		
							parent
							
								
									7d7209dd31
								
							
						
					
					
						commit
						5bfdb51676
					
				| @ -1,5 +1,15 @@ | ||||
| # standard imports | ||||
| import copy | ||||
| import re | ||||
| 
 | ||||
| 
 | ||||
| def is_valid_label(v, alpha_only=False): | ||||
|     re_m = None | ||||
|     if alpha_only: | ||||
|         re_m = r'^[a-zA-Z]+$' | ||||
|     else: | ||||
|         re_m = r'^[a-zA-Z0-9]+$' | ||||
|     return re.match(re_m, v) | ||||
| 
 | ||||
| 
 | ||||
| class ChainSpec: | ||||
| @ -16,13 +26,37 @@ class ChainSpec: | ||||
|     :param tag: Descriptive tag | ||||
|     :type tag: str | ||||
|     """ | ||||
|     def __init__(self, arch, fork, network_id, common_name=None): | ||||
|     def __init__(self, arch, fork, network_id, common_name=None, custom=[], safe=True): | ||||
|         if custom == None: | ||||
|             custom = [] | ||||
|         elif not isinstance(custom, list): | ||||
|             raise ValueError('custom value must be list') | ||||
| 
 | ||||
|         self.o = { | ||||
|                 'arch': arch, | ||||
|                 'fork': fork, | ||||
|                 'network_id': network_id, | ||||
|                 'common_name': common_name, | ||||
|                 } | ||||
|         'arch': arch, | ||||
|         'fork': fork, | ||||
|         'network_id': network_id, | ||||
|         'common_name': common_name, | ||||
|         'custom': custom, | ||||
|         } | ||||
| 
 | ||||
|         if safe: | ||||
|             self.validate() | ||||
| 
 | ||||
| 
 | ||||
|     def validate(self): | ||||
|         self.o['network_id'] = int(self.o['network_id']) | ||||
|         if not is_valid_label(self.o['arch'], alpha_only=True): | ||||
|             raise ValueError('arch: ' + self.o['arch']) | ||||
|         if not is_valid_label(self.o['fork'], alpha_only=True): | ||||
|             raise ValueError('fork: ' + self.o['fork']) | ||||
|         if self.o.get('common_name') and not is_valid_label(self.o['common_name']): | ||||
|             raise ValueError('common_name: ' + self.o['common_name']) | ||||
|         if self.o.get('custom'): | ||||
|             for i, v in enumerate(self.o['custom']): | ||||
|                 if not is_valid_label(v): | ||||
|                     raise ValueError('common_name {}: {}'.format(i, v)) | ||||
| 
 | ||||
| 
 | ||||
|     def network_id(self): | ||||
|         """Returns the network id part of the spec. | ||||
| @ -43,6 +77,12 @@ class ChainSpec: | ||||
| 
 | ||||
| 
 | ||||
|     def engine(self): | ||||
|         """Alias of self.arch() | ||||
|         """ | ||||
|         return self.arch() | ||||
| 
 | ||||
| 
 | ||||
|     def arch(self): | ||||
|         """Returns the chain architecture part of the spec | ||||
| 
 | ||||
|         :rtype: str | ||||
| @ -51,6 +91,15 @@ class ChainSpec: | ||||
|         return self.o['arch'] | ||||
| 
 | ||||
| 
 | ||||
|     def fork(self): | ||||
|         """Returns the fork part of the spec | ||||
| 
 | ||||
|         :rtype: str | ||||
|         :returns: fork | ||||
|         """ | ||||
|         return self.o['fork'] | ||||
| 
 | ||||
| 
 | ||||
|     def common_name(self): | ||||
|         """Returns the common name part of the spec | ||||
| 
 | ||||
| @ -60,6 +109,21 @@ class ChainSpec: | ||||
|         return self.o['common_name'] | ||||
| 
 | ||||
| 
 | ||||
|     def is_same_as(self, chain_spec_cmp, use_common_name=False, use_custom=False): | ||||
|         a = ['arch', 'fork', 'network_id'] | ||||
|         if use_common_name: | ||||
|             a += ['common_name'] | ||||
|         if use_custom: | ||||
|             a += ['custom'] | ||||
|         try: | ||||
|             for k in a: | ||||
|                 assert(chain_spec_cmp.o[k] == self.o[k]) | ||||
|         except AssertionError: | ||||
|             return False | ||||
|         return True | ||||
|                  | ||||
| 
 | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def from_chain_str(chain_str): | ||||
|         """Create a new ChainSpec object from a colon-separated string, as output by the string representation of the ChainSpec object. | ||||
| @ -79,9 +143,13 @@ class ChainSpec: | ||||
|         if len(o) < 3: | ||||
|             raise ValueError('Chain string must have three sections, got {}'.format(len(o))) | ||||
|         common_name = None | ||||
|         if len(o) == 4: | ||||
|         if len(o) > 3: | ||||
|             common_name = o[3] | ||||
|         return ChainSpec(o[0], o[1], int(o[2]), common_name) | ||||
|         custom = [] | ||||
|         if len(o) > 4: | ||||
|             for i in range(4, len(o)): | ||||
|                 custom.append(o[i]) | ||||
|         return ChainSpec(o[0], o[1], int(o[2]), common_name=common_name, custom=custom) | ||||
| 
 | ||||
| 
 | ||||
|     @staticmethod | ||||
| @ -100,20 +168,28 @@ class ChainSpec: | ||||
|         :rtype: chainlib.chain.ChainSpec | ||||
|         :returns: Resulting chain spec | ||||
|         """ | ||||
|         return ChainSpec(o['arch'], o['fork'], o['network_id'], common_name=o['common_name']) | ||||
|         return ChainSpec(o['arch'], o['fork'], o['network_id'], common_name=o.get('common_name'), custom=o.get('custom')) | ||||
| 
 | ||||
|    | ||||
|     def asdict(self): | ||||
|     def asdict(self, use_common_name=True, use_custom=True): | ||||
|         """Create a dictionary representation of the chain spec. | ||||
| 
 | ||||
|         :rtype: dict | ||||
|         :returns: Chain spec dictionary | ||||
|         """ | ||||
|         return copy.copy(self.o) | ||||
|         r = copy.copy(self.o) | ||||
|         if not use_common_name: | ||||
|             del r['common_name'] | ||||
|             del r['custom'] | ||||
|         if not use_custom: | ||||
|             del r['custom'] | ||||
|         return r | ||||
| 
 | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         s = '{}:{}:{}'.format(self.o['arch'], self.o['fork'], self.o['network_id']) | ||||
|         if self.o['common_name'] != None: | ||||
|         if self.o.get('common_name'): | ||||
|             s += ':' + self.o['common_name'] | ||||
|         if self.o.get('custom'): | ||||
|             s += ':' + ':'.join(self.o['custom']) | ||||
|         return s | ||||
|  | ||||
| @ -236,7 +236,8 @@ class Config(confini.Config): | ||||
|             if existing_r == None or r != None: | ||||
|                 config.add(r, v, exists_ok=True) | ||||
| 
 | ||||
|         if getattr(args, 'dumpconfig', None) != None: | ||||
|         logg.debug('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>' + str(getattr(args, 'dumpconfig'))) | ||||
|         if getattr(args, 'dumpconfig', None): | ||||
|             config_keys = config.all() | ||||
|             with_values = not config.get('_RAW') | ||||
|             for k in config_keys: | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| [metadata] | ||||
| name = chainlib | ||||
| version = 0.0.10a8 | ||||
| version = 0.0.10a9 | ||||
| description = Generic blockchain access library and tooling | ||||
| author = Louis Holbrook | ||||
| author_email = dev@holbrook.no | ||||
|  | ||||
| @ -1,40 +1,81 @@ | ||||
| # standard imports | ||||
| import unittest | ||||
| import logging | ||||
| 
 | ||||
| # local imports | ||||
| from chainlib.chain import ChainSpec | ||||
| 
 | ||||
| # test imports | ||||
| from tests.base import TestBase | ||||
| 
 | ||||
| logging.basicConfig(level=logging.DEBUG) | ||||
| logg = logging.getLogger() | ||||
| logg.setLevel(logging.DEBUG) | ||||
| 
 | ||||
| 
 | ||||
| class TestChain(TestBase): | ||||
| 
 | ||||
|     def test_chain_spec_str(self): | ||||
|         s = ChainSpec('foo', 'bar', 3, 'baz') | ||||
|         self.assertEqual('foo:bar:3:baz', str(s)) | ||||
| 
 | ||||
|         s = ChainSpec('foo', 'bar', 3) | ||||
|         self.assertEqual('foo:bar:3', str(s)) | ||||
| 
 | ||||
|     def test_chain_spec(self): | ||||
|         s = ChainSpec('foo', 'bar', 3, 'baz') | ||||
|         self.assertEqual('foo:bar:3:baz', str(s)) | ||||
| 
 | ||||
| 
 | ||||
|         s = ChainSpec('foo', 'bar', 3, 'baz', ['inky', 'pinky', 'blinky']) | ||||
|         self.assertEqual('foo:bar:3:baz:inky:pinky:blinky', str(s)) | ||||
| 
 | ||||
| 
 | ||||
|     def test_chain_spec(self): | ||||
|         s = ChainSpec.from_chain_str('foo:bar:3') | ||||
|         s = ChainSpec.from_chain_str('foo:bar:3:baz') | ||||
|         s = ChainSpec.from_chain_str('foo:bar:3:baz:inky:pinky:blinky') | ||||
| 
 | ||||
|         with self.assertRaises(ValueError): | ||||
|             s = ChainSpec.from_chain_str('foo:bar:a') | ||||
|             s = ChainSpec.from_chain_str('foo:bar') | ||||
|             s = ChainSpec.from_chain_str('foo') | ||||
|             s = ChainSpec.from_chain_str('foo1:bar:3') | ||||
|             s = ChainSpec.from_chain_str('foo:bar2:3') | ||||
| 
 | ||||
| 
 | ||||
|     def test_chain_spec_dict(self): | ||||
|         s = 'foo:bar:3:baz' | ||||
|         c = ChainSpec.from_chain_str('foo:bar:3:baz') | ||||
|         ss = 'foo:bar:3:baz:inky:pinky:blinky' | ||||
|         c = ChainSpec.from_chain_str(ss) | ||||
|         d = c.asdict() | ||||
|         self.assertEqual(d['arch'], 'foo') | ||||
|         self.assertEqual(d['fork'], 'bar') | ||||
|         self.assertEqual(d['network_id'], 3) | ||||
|         self.assertEqual(d['common_name'], 'baz') | ||||
|         self.assertEqual(d['custom'], ['inky', 'pinky', 'blinky']) | ||||
|         cc = ChainSpec.from_dict(d) | ||||
|         self.assertEqual(s, str(cc)) | ||||
|         self.assertEqual(ss, str(cc)) | ||||
| 
 | ||||
|         d = c.asdict(use_custom=False) | ||||
|         cc = ChainSpec.from_dict(d) | ||||
|         self.assertEqual(str(cc), 'foo:bar:3:baz') | ||||
| 
 | ||||
|         d = c.asdict(use_common_name=False) | ||||
|         cc = ChainSpec.from_dict(d) | ||||
|         self.assertEqual(str(cc), 'foo:bar:3') | ||||
| 
 | ||||
|     def test_chain_spec_compare(self): | ||||
|         a = 'foo:bar:42:baz' | ||||
|         b = 'foo:bar:42:barbar' | ||||
|         c = 'foo:bar:42:baz:inky:pinky:blinky' | ||||
| 
 | ||||
|         ca = ChainSpec.from_chain_str(a) | ||||
|         cb = ChainSpec.from_chain_str(b) | ||||
| 
 | ||||
|         self.assertTrue(ca.is_same_as(cb)) | ||||
|         self.assertFalse(ca.is_same_as(cb, use_common_name=True)) | ||||
| 
 | ||||
|         cc = ChainSpec.from_chain_str(c) | ||||
|         logg.debug('chain_spec_cmp ' + str(cc.o)) | ||||
|         self.assertTrue(ca.is_same_as(cc)) | ||||
|         self.assertTrue(ca.is_same_as(cc, use_common_name=True)) | ||||
|         self.assertFalse(ca.is_same_as(cc, use_common_name=True, use_custom=True)) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| # standard imports | ||||
| import unittest | ||||
| import os | ||||
| import logging | ||||
| 
 | ||||
| # local imports | ||||
| import chainlib.cli | ||||
| @ -10,6 +11,8 @@ script_dir = os.path.dirname(os.path.realpath(__file__)) | ||||
| data_dir = os.path.join(script_dir, 'testdata') | ||||
| config_dir = os.path.join(data_dir, 'config') | ||||
| 
 | ||||
| logging.basicConfig(level=logging.DEBUG) | ||||
| 
 | ||||
| 
 | ||||
| class TestCli(unittest.TestCase): | ||||
|      | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user