forked from mirrors/0ad
Lint and format Python files using ruff
To improve quality und uniformity of the included Python code this lints and formats the included Python files with ruff.
This commit is contained in:
@@ -4,31 +4,36 @@ import zero_ad
|
||||
|
||||
# First, we will define some helper functions we will use later.
|
||||
import math
|
||||
def dist (p1, p2):
|
||||
|
||||
|
||||
def dist(p1, p2):
|
||||
return math.sqrt(sum((math.pow(x2 - x1, 2) for (x1, x2) in zip(p1, p2))))
|
||||
|
||||
|
||||
def center(units):
|
||||
sum_position = map(sum, zip(*map(lambda u: u.position(), units)))
|
||||
return [x/len(units) for x in sum_position]
|
||||
return [x / len(units) for x in sum_position]
|
||||
|
||||
|
||||
def closest(units, position):
|
||||
dists = (dist(unit.position(), position) for unit in units)
|
||||
index = 0
|
||||
min_dist = next(dists)
|
||||
for (i, d) in enumerate(dists):
|
||||
for i, d in enumerate(dists):
|
||||
if d < min_dist:
|
||||
index = i
|
||||
min_dist = d
|
||||
|
||||
return units[index]
|
||||
|
||||
|
||||
# Connect to a 0 AD game server listening at localhost:6000
|
||||
game = zero_ad.ZeroAD('http://localhost:6000')
|
||||
game = zero_ad.ZeroAD("http://localhost:6000")
|
||||
|
||||
# Load the Arcadia map
|
||||
samples_dir = path.dirname(path.realpath(__file__))
|
||||
scenario_config_path = path.join(samples_dir, 'arcadia.json')
|
||||
with open(scenario_config_path, 'r') as f:
|
||||
scenario_config_path = path.join(samples_dir, "arcadia.json")
|
||||
with open(scenario_config_path, "r") as f:
|
||||
arcadia_config = f.read()
|
||||
|
||||
state = game.reset(arcadia_config)
|
||||
@@ -37,15 +42,15 @@ state = game.reset(arcadia_config)
|
||||
state = game.step()
|
||||
|
||||
# Units can be queried from the game state
|
||||
citizen_soldiers = state.units(owner=1, type='infantry')
|
||||
citizen_soldiers = state.units(owner=1, type="infantry")
|
||||
# (including gaia units like trees or other resources)
|
||||
nearby_tree = closest(state.units(owner=0, type='tree'), center(citizen_soldiers))
|
||||
nearby_tree = closest(state.units(owner=0, type="tree"), center(citizen_soldiers))
|
||||
|
||||
# Action commands can be created using zero_ad.actions
|
||||
collect_wood = zero_ad.actions.gather(citizen_soldiers, nearby_tree)
|
||||
|
||||
female_citizens = state.units(owner=1, type='female_citizen')
|
||||
house_tpl = 'structures/spart/house'
|
||||
female_citizens = state.units(owner=1, type="female_citizen")
|
||||
house_tpl = "structures/spart/house"
|
||||
x = 680
|
||||
z = 640
|
||||
build_house = zero_ad.actions.construct(female_citizens, house_tpl, x, z, autocontinue=True)
|
||||
@@ -58,20 +63,24 @@ female_id = female_citizens[0].id()
|
||||
female_citizen = state.unit(female_id)
|
||||
|
||||
# A variety of unit information can be queried from the unit:
|
||||
print('female citizen\'s max health is', female_citizen.max_health())
|
||||
print("female citizen's max health is", female_citizen.max_health())
|
||||
|
||||
# Raw data for units and game states are available via the data attribute
|
||||
print(female_citizen.data)
|
||||
|
||||
# Units can be built using the "train action"
|
||||
civic_center = state.units(owner=1, type="civil_centre")[0]
|
||||
spearman_type = 'units/spart/infantry_spearman_b'
|
||||
spearman_type = "units/spart/infantry_spearman_b"
|
||||
train_spearmen = zero_ad.actions.train([civic_center], spearman_type)
|
||||
|
||||
state = game.step([train_spearmen])
|
||||
|
||||
|
||||
# Let's step the engine until the house has been built
|
||||
is_unit_busy = lambda state, unit_id: len(state.unit(unit_id).data['unitAIOrderData']) > 0
|
||||
def is_unit_busy(state, unit_id):
|
||||
return len(state.unit(unit_id).data["unitAIOrderData"]) > 0
|
||||
|
||||
|
||||
while is_unit_busy(state, female_id):
|
||||
state = game.step()
|
||||
|
||||
@@ -85,14 +94,16 @@ for _ in range(150):
|
||||
state = game.step()
|
||||
|
||||
# Let's attack with our entire military
|
||||
state = game.step([zero_ad.actions.chat('An attack is coming!')])
|
||||
state = game.step([zero_ad.actions.chat("An attack is coming!")])
|
||||
|
||||
while len(state.units(owner=2, type='unit')) > 0:
|
||||
attack_units = [ unit for unit in state.units(owner=1, type='unit') if 'female' not in unit.type() ]
|
||||
target = closest(state.units(owner=2, type='unit'), center(attack_units))
|
||||
while len(state.units(owner=2, type="unit")) > 0:
|
||||
attack_units = [
|
||||
unit for unit in state.units(owner=1, type="unit") if "female" not in unit.type()
|
||||
]
|
||||
target = closest(state.units(owner=2, type="unit"), center(attack_units))
|
||||
state = game.step([zero_ad.actions.attack(attack_units, target)])
|
||||
|
||||
while state.unit(target.id()):
|
||||
state = game.step()
|
||||
|
||||
game.step([zero_ad.actions.chat('The enemies have been vanquished. Our home is safe again.')])
|
||||
game.step([zero_ad.actions.chat("The enemies have been vanquished. Our home is safe again.")])
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import os
|
||||
from setuptools import setup
|
||||
|
||||
setup(name='zero_ad',
|
||||
version='0.0.1',
|
||||
description='Python client for 0 AD',
|
||||
url='https://code.wildfiregames.com',
|
||||
author='Brian Broll',
|
||||
author_email='brian.broll@gmail.com',
|
||||
install_requires=[],
|
||||
license='MIT',
|
||||
packages=['zero_ad'],
|
||||
zip_safe=False)
|
||||
setup(
|
||||
name="zero_ad",
|
||||
version="0.0.1",
|
||||
description="Python client for 0 AD",
|
||||
url="https://code.wildfiregames.com",
|
||||
author="Brian Broll",
|
||||
author_email="brian.broll@gmail.com",
|
||||
install_requires=[],
|
||||
license="MIT",
|
||||
packages=["zero_ad"],
|
||||
zip_safe=False,
|
||||
)
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
import zero_ad
|
||||
import json
|
||||
import math
|
||||
from os import path
|
||||
|
||||
game = zero_ad.ZeroAD('http://localhost:6000')
|
||||
game = zero_ad.ZeroAD("http://localhost:6000")
|
||||
scriptdir = path.dirname(path.realpath(__file__))
|
||||
with open(path.join(scriptdir, '..', 'samples', 'arcadia.json'), 'r') as f:
|
||||
with open(path.join(scriptdir, "..", "samples", "arcadia.json"), "r") as f:
|
||||
config = f.read()
|
||||
|
||||
def dist (p1, p2):
|
||||
|
||||
def dist(p1, p2):
|
||||
return math.sqrt(sum((math.pow(x2 - x1, 2) for (x1, x2) in zip(p1, p2))))
|
||||
|
||||
|
||||
def center(units):
|
||||
sum_position = map(sum, zip(*map(lambda u: u.position(), units)))
|
||||
return [x/len(units) for x in sum_position]
|
||||
return [x / len(units) for x in sum_position]
|
||||
|
||||
|
||||
def closest(units, position):
|
||||
dists = (dist(unit.position(), position) for unit in units)
|
||||
index = 0
|
||||
min_dist = next(dists)
|
||||
for (i, d) in enumerate(dists):
|
||||
for i, d in enumerate(dists):
|
||||
if d < min_dist:
|
||||
index = i
|
||||
min_dist = d
|
||||
|
||||
return units[index]
|
||||
|
||||
|
||||
def test_construct():
|
||||
state = game.reset(config)
|
||||
female_citizens = state.units(owner=1, type='female_citizen')
|
||||
house_tpl = 'structures/spart/house'
|
||||
female_citizens = state.units(owner=1, type="female_citizen")
|
||||
house_tpl = "structures/spart/house"
|
||||
house_count = len(state.units(owner=1, type=house_tpl))
|
||||
x = 680
|
||||
z = 640
|
||||
@@ -39,21 +42,23 @@ def test_construct():
|
||||
while len(state.units(owner=1, type=house_tpl)) == house_count:
|
||||
state = game.step()
|
||||
|
||||
|
||||
def test_gather():
|
||||
state = game.reset(config)
|
||||
female_citizen = state.units(owner=1, type='female_citizen')[0]
|
||||
trees = state.units(owner=0, type='tree')
|
||||
nearby_tree = closest(state.units(owner=0, type='tree'), female_citizen.position())
|
||||
female_citizen = state.units(owner=1, type="female_citizen")[0]
|
||||
state.units(owner=0, type="tree")
|
||||
nearby_tree = closest(state.units(owner=0, type="tree"), female_citizen.position())
|
||||
|
||||
collect_wood = zero_ad.actions.gather([female_citizen], nearby_tree)
|
||||
state = game.step([collect_wood])
|
||||
while len(state.unit(female_citizen.id()).data['resourceCarrying']) == 0:
|
||||
while len(state.unit(female_citizen.id()).data["resourceCarrying"]) == 0:
|
||||
state = game.step()
|
||||
|
||||
|
||||
def test_train():
|
||||
state = game.reset(config)
|
||||
civic_centers = state.units(owner=1, type="civil_centre")
|
||||
spearman_type = 'units/spart/infantry_spearman_b'
|
||||
spearman_type = "units/spart/infantry_spearman_b"
|
||||
spearman_count = len(state.units(owner=1, type=spearman_type))
|
||||
train_spearmen = zero_ad.actions.train(civic_centers, spearman_type)
|
||||
|
||||
@@ -61,9 +66,10 @@ def test_train():
|
||||
while len(state.units(owner=1, type=spearman_type)) == spearman_count:
|
||||
state = game.step()
|
||||
|
||||
|
||||
def test_walk():
|
||||
state = game.reset(config)
|
||||
female_citizens = state.units(owner=1, type='female_citizen')
|
||||
female_citizens = state.units(owner=1, type="female_citizen")
|
||||
x = 680
|
||||
z = 640
|
||||
initial_distance = dist(center(female_citizens), [x, z])
|
||||
@@ -73,13 +79,14 @@ def test_walk():
|
||||
distance = initial_distance
|
||||
while distance >= initial_distance:
|
||||
state = game.step()
|
||||
female_citizens = state.units(owner=1, type='female_citizen')
|
||||
female_citizens = state.units(owner=1, type="female_citizen")
|
||||
distance = dist(center(female_citizens), [x, z])
|
||||
|
||||
|
||||
def test_attack():
|
||||
state = game.reset(config)
|
||||
unit = state.units(owner=1, type='cavalry')[0]
|
||||
target = state.units(owner=2, type='female_citizen')[0]
|
||||
unit = state.units(owner=1, type="cavalry")[0]
|
||||
target = state.units(owner=2, type="female_citizen")[0]
|
||||
initial_health_target = target.health()
|
||||
initial_health_unit = unit.health()
|
||||
|
||||
@@ -87,11 +94,13 @@ def test_attack():
|
||||
|
||||
attack = zero_ad.actions.attack([unit], target)
|
||||
state = game.step([attack])
|
||||
while (state.unit(target.id()).health() >= initial_health_target
|
||||
) and (state.unit(unit.id()).health() >= initial_health_unit):
|
||||
while (state.unit(target.id()).health() >= initial_health_target) and (
|
||||
state.unit(unit.id()).health() >= initial_health_unit
|
||||
):
|
||||
state = game.step()
|
||||
|
||||
|
||||
def test_chat():
|
||||
state = game.reset(config)
|
||||
chat = zero_ad.actions.chat('hello world!!')
|
||||
state = game.step([chat])
|
||||
game.reset(config)
|
||||
chat = zero_ad.actions.chat("hello world!!")
|
||||
game.step([chat])
|
||||
|
||||
@@ -1,44 +1,48 @@
|
||||
import zero_ad
|
||||
import json
|
||||
import math
|
||||
from os import path
|
||||
|
||||
game = zero_ad.ZeroAD('http://localhost:6000')
|
||||
game = zero_ad.ZeroAD("http://localhost:6000")
|
||||
scriptdir = path.dirname(path.realpath(__file__))
|
||||
with open(path.join(scriptdir, '..', 'samples', 'arcadia.json'), 'r') as f:
|
||||
with open(path.join(scriptdir, "..", "samples", "arcadia.json"), "r") as f:
|
||||
config = f.read()
|
||||
|
||||
with open(path.join(scriptdir, 'fastactions.js'), 'r') as f:
|
||||
with open(path.join(scriptdir, "fastactions.js"), "r") as f:
|
||||
fastactions = f.read()
|
||||
|
||||
|
||||
def test_return_object():
|
||||
state = game.reset(config)
|
||||
game.reset(config)
|
||||
result = game.evaluate('({"hello": "world"})')
|
||||
assert type(result) is dict
|
||||
assert result['hello'] == 'world'
|
||||
assert result["hello"] == "world"
|
||||
|
||||
|
||||
def test_return_null():
|
||||
result = game.evaluate('null')
|
||||
assert result == None
|
||||
result = game.evaluate("null")
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_return_string():
|
||||
state = game.reset(config)
|
||||
game.reset(config)
|
||||
result = game.evaluate('"cat"')
|
||||
assert result == 'cat'
|
||||
assert result == "cat"
|
||||
|
||||
|
||||
def test_fastactions():
|
||||
state = game.reset(config)
|
||||
game.evaluate(fastactions)
|
||||
female_citizens = state.units(owner=1, type='female_citizen')
|
||||
house_tpl = 'structures/spart/house'
|
||||
house_count = len(state.units(owner=1, type=house_tpl))
|
||||
female_citizens = state.units(owner=1, type="female_citizen")
|
||||
house_tpl = "structures/spart/house"
|
||||
len(state.units(owner=1, type=house_tpl))
|
||||
x = 680
|
||||
z = 640
|
||||
build_house = zero_ad.actions.construct(female_citizens, house_tpl, x, z, autocontinue=True)
|
||||
# Check that they start building the house
|
||||
state = game.step([build_house])
|
||||
step_count = 0
|
||||
new_house = lambda _=None: state.units(owner=1, type=house_tpl)[0]
|
||||
|
||||
def new_house(_=None):
|
||||
return state.units(owner=1, type=house_tpl)[0]
|
||||
|
||||
initial_health = new_house().health(ratio=True)
|
||||
while new_house().health(ratio=True) == initial_health:
|
||||
state = game.step()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from . import actions
|
||||
from . import actions # noqa: F401
|
||||
from . import environment
|
||||
|
||||
ZeroAD = environment.ZeroAD
|
||||
GameState = environment.GameState
|
||||
|
||||
@@ -1,63 +1,57 @@
|
||||
def construct(units, template, x, z, angle=0, autorepair=True, autocontinue=True, queued=False):
|
||||
unit_ids = [ unit.id() for unit in units ]
|
||||
unit_ids = [unit.id() for unit in units]
|
||||
return {
|
||||
'type': 'construct',
|
||||
'entities': unit_ids,
|
||||
'template': template,
|
||||
'x': x,
|
||||
'z': z,
|
||||
'angle': angle,
|
||||
'autorepair': autorepair,
|
||||
'autocontinue': autocontinue,
|
||||
'queued': queued,
|
||||
"type": "construct",
|
||||
"entities": unit_ids,
|
||||
"template": template,
|
||||
"x": x,
|
||||
"z": z,
|
||||
"angle": angle,
|
||||
"autorepair": autorepair,
|
||||
"autocontinue": autocontinue,
|
||||
"queued": queued,
|
||||
}
|
||||
|
||||
|
||||
def gather(units, target, queued=False):
|
||||
unit_ids = [ unit.id() for unit in units ]
|
||||
unit_ids = [unit.id() for unit in units]
|
||||
return {
|
||||
'type': 'gather',
|
||||
'entities': unit_ids,
|
||||
'target': target.id(),
|
||||
'queued': queued,
|
||||
"type": "gather",
|
||||
"entities": unit_ids,
|
||||
"target": target.id(),
|
||||
"queued": queued,
|
||||
}
|
||||
|
||||
|
||||
def train(entities, unit_type, count=1):
|
||||
entity_ids = [ unit.id() for unit in entities ]
|
||||
entity_ids = [unit.id() for unit in entities]
|
||||
return {
|
||||
'type': 'train',
|
||||
'entities': entity_ids,
|
||||
'template': unit_type,
|
||||
'count': count,
|
||||
"type": "train",
|
||||
"entities": entity_ids,
|
||||
"template": unit_type,
|
||||
"count": count,
|
||||
}
|
||||
|
||||
|
||||
def chat(message):
|
||||
return {
|
||||
'type': 'aichat',
|
||||
'message': message
|
||||
}
|
||||
return {"type": "aichat", "message": message}
|
||||
|
||||
|
||||
def reveal_map():
|
||||
return {
|
||||
'type': 'reveal-map',
|
||||
'enable': True
|
||||
}
|
||||
return {"type": "reveal-map", "enable": True}
|
||||
|
||||
|
||||
def walk(units, x, z, queued=False):
|
||||
ids = [ unit.id() for unit in units ]
|
||||
return {
|
||||
'type': 'walk',
|
||||
'entities': ids,
|
||||
'x': x,
|
||||
'z': z,
|
||||
'queued': queued
|
||||
}
|
||||
ids = [unit.id() for unit in units]
|
||||
return {"type": "walk", "entities": ids, "x": x, "z": z, "queued": queued}
|
||||
|
||||
|
||||
def attack(units, target, queued=False, allow_capture=True):
|
||||
unit_ids = [ unit.id() for unit in units ]
|
||||
unit_ids = [unit.id() for unit in units]
|
||||
return {
|
||||
'type': 'attack',
|
||||
'entities': unit_ids,
|
||||
'target': target.id(),
|
||||
'allowCapture': allow_capture,
|
||||
'queued': queued
|
||||
"type": "attack",
|
||||
"entities": unit_ids,
|
||||
"target": target.id(),
|
||||
"allowCapture": allow_capture,
|
||||
"queued": queued,
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import urllib
|
||||
from urllib import request
|
||||
import json
|
||||
|
||||
class RLAPI():
|
||||
|
||||
class RLAPI:
|
||||
def __init__(self, url):
|
||||
self.url = url
|
||||
|
||||
def post(self, route, data):
|
||||
response = request.urlopen(url=f'{self.url}/{route}', data=bytes(data, 'utf8'))
|
||||
response = request.urlopen(url=f"{self.url}/{route}", data=bytes(data, "utf8"))
|
||||
return response.read()
|
||||
|
||||
def step(self, commands):
|
||||
post_data = '\n'.join((f'{player};{json.dumps(action)}' for (player, action) in commands))
|
||||
return self.post('step', post_data)
|
||||
post_data = "\n".join((f"{player};{json.dumps(action)}" for (player, action) in commands))
|
||||
return self.post("step", post_data)
|
||||
|
||||
def reset(self, scenario_config, player_id, save_replay):
|
||||
path = 'reset?'
|
||||
path = "reset?"
|
||||
if save_replay:
|
||||
path += 'saveReplay=1&'
|
||||
path += "saveReplay=1&"
|
||||
if player_id:
|
||||
path += f'playerID={player_id}&'
|
||||
path += f"playerID={player_id}&"
|
||||
|
||||
return self.post(path, scenario_config)
|
||||
|
||||
def get_templates(self, names):
|
||||
post_data = '\n'.join(names)
|
||||
response = self.post('templates', post_data)
|
||||
return zip(names, response.decode().split('\n'))
|
||||
post_data = "\n".join(names)
|
||||
response = self.post("templates", post_data)
|
||||
return zip(names, response.decode().split("\n"))
|
||||
|
||||
def evaluate(self, code):
|
||||
response = self.post('evaluate', code)
|
||||
response = self.post("evaluate", code)
|
||||
return json.loads(response.decode())
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from .api import RLAPI
|
||||
import json
|
||||
import math
|
||||
from xml.etree import ElementTree
|
||||
from itertools import cycle
|
||||
|
||||
class ZeroAD():
|
||||
def __init__(self, uri='http://localhost:6000'):
|
||||
|
||||
class ZeroAD:
|
||||
def __init__(self, uri="http://localhost:6000"):
|
||||
self.api = RLAPI(uri)
|
||||
self.current_state = None
|
||||
self.cache = {}
|
||||
@@ -20,7 +20,7 @@ class ZeroAD():
|
||||
self.current_state = GameState(json.loads(state_json), self)
|
||||
return self.current_state
|
||||
|
||||
def reset(self, config='', save_replay=False, player_id=1):
|
||||
def reset(self, config="", save_replay=False, player_id=1):
|
||||
state_json = self.api.reset(config, player_id, save_replay)
|
||||
self.current_state = GameState(json.loads(state_json), self)
|
||||
return self.current_state
|
||||
@@ -33,7 +33,7 @@ class ZeroAD():
|
||||
|
||||
def get_templates(self, names):
|
||||
templates = self.api.get_templates(names)
|
||||
return [ (name, EntityTemplate(content)) for (name, content) in templates ]
|
||||
return [(name, EntityTemplate(content)) for (name, content) in templates]
|
||||
|
||||
def update_templates(self, types=[]):
|
||||
all_types = list(set([unit.type() for unit in self.current_state.units()]))
|
||||
@@ -41,54 +41,60 @@ class ZeroAD():
|
||||
template_pairs = self.get_templates(all_types)
|
||||
|
||||
self.cache = {}
|
||||
for (name, tpl) in template_pairs:
|
||||
for name, tpl in template_pairs:
|
||||
self.cache[name] = tpl
|
||||
|
||||
return template_pairs
|
||||
|
||||
class GameState():
|
||||
|
||||
class GameState:
|
||||
def __init__(self, data, game):
|
||||
self.data = data
|
||||
self.game = game
|
||||
self.mapSize = self.data['mapSize']
|
||||
self.mapSize = self.data["mapSize"]
|
||||
|
||||
def units(self, owner=None, type=None):
|
||||
filter_fn = lambda e: (owner is None or e['owner'] == owner) and \
|
||||
(type is None or type in e['template'])
|
||||
return [ Entity(e, self.game) for e in self.data['entities'].values() if filter_fn(e) ]
|
||||
def filter_fn(e):
|
||||
return (owner is None or e["owner"] == owner) and (
|
||||
type is None or type in e["template"]
|
||||
)
|
||||
|
||||
return [Entity(e, self.game) for e in self.data["entities"].values() if filter_fn(e)]
|
||||
|
||||
def unit(self, id):
|
||||
id = str(id)
|
||||
return Entity(self.data['entities'][id], self.game) if id in self.data['entities'] else None
|
||||
return (
|
||||
Entity(self.data["entities"][id], self.game) if id in self.data["entities"] else None
|
||||
)
|
||||
|
||||
class Entity():
|
||||
|
||||
class Entity:
|
||||
def __init__(self, data, game):
|
||||
self.data = data
|
||||
self.game = game
|
||||
self.template = self.game.cache.get(self.type(), None)
|
||||
|
||||
def type(self):
|
||||
return self.data['template']
|
||||
return self.data["template"]
|
||||
|
||||
def id(self):
|
||||
return self.data['id']
|
||||
return self.data["id"]
|
||||
|
||||
def owner(self):
|
||||
return self.data['owner']
|
||||
return self.data["owner"]
|
||||
|
||||
def max_health(self):
|
||||
template = self.get_template()
|
||||
return float(template.get('Health/Max'))
|
||||
return float(template.get("Health/Max"))
|
||||
|
||||
def health(self, ratio=False):
|
||||
if ratio:
|
||||
return self.data['hitpoints']/self.max_health()
|
||||
return self.data["hitpoints"] / self.max_health()
|
||||
|
||||
return self.data['hitpoints']
|
||||
return self.data["hitpoints"]
|
||||
|
||||
def position(self):
|
||||
return self.data['position']
|
||||
return self.data["position"]
|
||||
|
||||
def get_template(self):
|
||||
if self.template is None:
|
||||
@@ -97,9 +103,10 @@ class Entity():
|
||||
|
||||
return self.template
|
||||
|
||||
class EntityTemplate():
|
||||
|
||||
class EntityTemplate:
|
||||
def __init__(self, xml):
|
||||
self.data = ElementTree.fromstring(f'<Entity>{xml}</Entity>')
|
||||
self.data = ElementTree.fromstring(f"<Entity>{xml}</Entity>")
|
||||
|
||||
def get(self, path):
|
||||
node = self.data.find(path)
|
||||
@@ -113,4 +120,4 @@ class EntityTemplate():
|
||||
return node is not None
|
||||
|
||||
def __str__(self):
|
||||
return ElementTree.tostring(self.data).decode('utf-8')
|
||||
return ElementTree.tostring(self.data).decode("utf-8")
|
||||
|
||||
Reference in New Issue
Block a user