Merge pull request #365 from coreemu/coretk-enhance/gui-logging

Coretk enhance/gui logging
This commit is contained in:
bharnden 2020-02-04 09:52:55 -08:00 committed by GitHub
commit 540e1b46d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 135 additions and 81 deletions

View file

@ -167,7 +167,6 @@ class CoreClient:
return
if event.HasField("link_event"):
logging.info("link event: %s", event)
self.handle_link_event(event.link_event)
elif event.HasField("session_event"):
logging.info("session event: %s", event)
@ -196,6 +195,7 @@ class CoreClient:
logging.info("unhandled event: %s", event)
def handle_link_event(self, event: core_pb2.LinkEvent):
logging.debug("Link event: %s", event)
node_one_id = event.link.node_one_id
node_two_id = event.link.node_two_id
canvas_node_one = self.canvas_nodes[node_one_id]
@ -209,6 +209,7 @@ class CoreClient:
logging.warning("unknown link event: %s", event.message_type)
def handle_node_event(self, event: core_pb2.NodeEvent):
logging.debug("node event: %s", event)
if event.source == GUI_SOURCE:
return
node_id = event.node.id
@ -234,7 +235,7 @@ class CoreClient:
self.session_id,
)
return
logging.info("handling throughputs event: %s", event)
logging.debug("handling throughputs event: %s", event)
self.app.canvas.set_throughputs(event)
def handle_exception_event(self, event: core_pb2.ExceptionEvent):
@ -242,6 +243,7 @@ class CoreClient:
self.app.statusbar.core_alarms.append(event)
def join_session(self, session_id: int, query_location: bool = True):
logging.info("join session(%s)", session_id)
# update session and title
self.session_id = session_id
self.master.title(f"CORE Session({self.session_id})")
@ -303,7 +305,7 @@ class CoreClient:
for config in response.configs:
service_configs = self.service_configs.setdefault(config.node_id, {})
service_configs[config.service] = config.data
logging.info("service file configs: %s", config.files)
logging.debug("service file configs: %s", config.files)
for file_name in config.files:
file_configs = self.file_configs.setdefault(config.node_id, {})
files = file_configs.setdefault(config.service, {})
@ -341,7 +343,7 @@ class CoreClient:
def parse_metadata(self, config: Dict[str, str]):
# canvas setting
canvas_config = config.get("canvas")
logging.info("canvas metadata: %s", canvas_config)
logging.debug("canvas metadata: %s", canvas_config)
if canvas_config:
canvas_config = json.loads(canvas_config)
@ -425,7 +427,7 @@ class CoreClient:
session_id = self.session_id
try:
response = self.client.delete_session(session_id)
logging.info("deleted session(%s) result: %s", session_id, response)
logging.info("deleted session(%s), Result: %s", session_id, response)
except grpc.RpcError as e:
self.app.after(0, show_grpc_error, e)
@ -435,7 +437,6 @@ class CoreClient:
"""
try:
self.client.connect()
# get service information
response = self.client.get_services()
for service in response.services:
@ -453,7 +454,6 @@ class CoreClient:
# if there are no sessions, create a new session, else join a session
response = self.client.get_sessions()
logging.info("current sessions: %s", response)
sessions = response.sessions
if len(sessions) == 0:
self.create_new_session()
@ -511,7 +511,7 @@ class CoreClient:
asymmetric_links,
config_service_configs,
)
logging.debug(
logging.info(
"start session(%s), result: %s", self.session_id, response.result
)
@ -527,7 +527,7 @@ class CoreClient:
response = core_pb2.StopSessionResponse(result=False)
try:
response = self.client.stop_session(session_id)
logging.debug("stopped session(%s), result: %s", session_id, response)
logging.info("stopped session(%s), result: %s", session_id, response)
except grpc.RpcError as e:
self.app.after(0, show_grpc_error, e)
return response
@ -561,7 +561,7 @@ class CoreClient:
metadata = {"canvas": canvas_config, "shapes": shapes}
response = self.client.set_session_metadata(self.session_id, metadata)
logging.info("set session metadata: %s", response)
logging.info("set session metadata %s, result: %s", metadata, response)
def launch_terminal(self, node_id: int):
try:
@ -582,12 +582,10 @@ class CoreClient:
"""
try:
if self.state != core_pb2.SessionState.RUNTIME:
logging.debug(
"session state not runtime, send session data to the daemon..."
)
logging.debug("Send session data to the daemon")
self.send_data()
response = self.client.save_xml(self.session_id, file_path)
logging.info("saved xml(%s): %s", file_path, response)
logging.info("saved xml file %s, result: %s", file_path, response)
except grpc.RpcError as e:
self.app.after(0, show_grpc_error, e)
@ -597,7 +595,7 @@ class CoreClient:
"""
try:
response = self.client.open_xml(file_path)
logging.debug("open xml: %s", response)
logging.info("open xml file %s, response: %s", file_path, response)
self.join_session(response.session_id)
except grpc.RpcError as e:
self.app.after(0, show_grpc_error, e)
@ -606,7 +604,9 @@ class CoreClient:
self, node_id: int, service_name: str
) -> core_pb2.NodeServiceData:
response = self.client.get_node_service(self.session_id, node_id, service_name)
logging.debug("get node service %s", response)
logging.debug(
"get node(%s) %s service, response: %s", node_id, service_name, response
)
return response.service
def set_node_service(
@ -620,9 +620,16 @@ class CoreClient:
response = self.client.set_node_service(
self.session_id, node_id, service_name, startups, validations, shutdowns
)
logging.debug("set node service %s", response)
logging.info(
"Set %s service for node(%s), Startup: %s, Validation: %s, Shutdown: %s, Result: %s",
service_name,
node_id,
startups,
validations,
shutdowns,
response,
)
response = self.client.get_node_service(self.session_id, node_id, service_name)
logging.debug("get node service : %s", response)
return response.service
def get_node_service_file(
@ -631,7 +638,13 @@ class CoreClient:
response = self.client.get_node_service_file(
self.session_id, node_id, service_name, file_name
)
logging.debug("get service file %s", response)
logging.debug(
"get service file for node(%s), service: %s, file: %s, result: %s",
node_id,
service_name,
file_name,
response,
)
return response.data
def set_node_service_file(
@ -640,7 +653,14 @@ class CoreClient:
response = self.client.set_node_service_file(
self.session_id, node_id, service_name, file_name, data
)
logging.debug("set node service file %s", response)
logging.info(
"set node(%s) service file, service: %s, file: %s, data: %s, result: %s",
node_id,
service_name,
file_name,
data,
response,
)
def create_nodes_and_links(self):
"""
@ -733,7 +753,7 @@ class CoreClient:
return i
def create_node(
self, x: int, y: int, node_type: core_pb2.NodeType, model: str
self, x: float, y: float, node_type: core_pb2.NodeType, model: str
) -> core_pb2.Node:
"""
Add node, with information filled in, to grpc manager
@ -765,12 +785,12 @@ class CoreClient:
if NodeUtils.is_custom(model):
services = NodeUtils.get_custom_node_services(self.app.guiconfig, model)
node.services[:] = services
logging.debug(
"adding node to core session: %s, coords: (%s, %s), name: %s",
logging.info(
"add node(%s) to session(%s), coordinates(%s, %s)",
node.name,
self.session_id,
x,
y,
node.name,
)
return node
@ -860,6 +880,7 @@ class CoreClient:
)
edge.set_link(link)
self.links[edge.token] = edge
logging.info("Add link between %s and %s", src_node.name, dst_node.name)
def get_wlan_configs_proto(self) -> List[core_pb2.WlanConfig]:
configs = []
@ -940,6 +961,11 @@ class CoreClient:
if not config:
response = self.client.get_wlan_config(self.session_id, node_id)
config = response.config
logging.debug(
"get wlan configuration from node %s, result configuration: %s",
node_id,
config,
)
return config
def get_mobility_config(self, node_id: int) -> Dict[str, common_pb2.ConfigOption]:
@ -947,12 +973,16 @@ class CoreClient:
if not config:
response = self.client.get_mobility_config(self.session_id, node_id)
config = response.config
logging.debug(
"get mobility config from node %s, result configuration: %s",
node_id,
config,
)
return config
def get_emane_model_config(
self, node_id: int, model: str, interface: int = None
) -> Dict[str, common_pb2.ConfigOption]:
logging.info("getting emane model config: %s %s %s", node_id, model, interface)
config = self.emane_model_configs.get((node_id, model, interface))
if not config:
if interface is None:
@ -961,6 +991,13 @@ class CoreClient:
self.session_id, node_id, model, interface
)
config = response.config
logging.debug(
"get emane model config: node id: %s, EMANE model: %s, interface: %s, config: %s",
node_id,
model,
interface,
config,
)
return config
def set_emane_model_config(
@ -970,12 +1007,19 @@ class CoreClient:
config: Dict[str, common_pb2.ConfigOption],
interface: int = None,
):
logging.info("setting emane model config: %s %s %s", node_id, model, interface)
logging.info(
"set emane model config: node id: %s, EMANE Model: %s, interface: %s, config: %s",
node_id,
model,
interface,
config,
)
self.emane_model_configs[(node_id, model, interface)] = config
def copy_node_service(self, _from: int, _to: int):
services = self.canvas_nodes[_from].core_node.services
self.canvas_nodes[_to].core_node.services[:] = services
logging.debug("copying node %s service to node %s", _from, _to)
def copy_node_config(self, _from: int, _to: int):
node_type = self.canvas_nodes[_from].core_node.type

View file

@ -1,7 +1,6 @@
"""
custom color picker
"""
import logging
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING, Any
@ -175,7 +174,6 @@ class ColorPickerDialog(Dialog):
self.hex.trace_add("write", self.update_color)
def button_ok(self):
logging.debug("not implemented")
self.color = self.hex.get()
self.destroy()

View file

@ -2,7 +2,6 @@
copy service config dialog
"""
import logging
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING, Any, Tuple
@ -91,7 +90,6 @@ class CopyServiceConfigDialog(Dialog):
button.grid(row=0, column=2, sticky="ew", padx=PADX)
def click_copy(self):
logging.debug("click copy")
selected = self.tree.selection()
if selected:
item = self.tree.item(selected[0])

View file

@ -218,6 +218,12 @@ class CustomNodesDialog(Dialog):
if name not in self.app.core.custom_nodes:
image_file = Path(self.image_file).stem
node_draw = NodeDraw.from_custom(name, image_file, set(self.services))
logging.info(
"created new custom node (%s), image file (%s), services: (%s)",
name,
image_file,
self.services,
)
self.app.core.custom_nodes[name] = node_draw
self.nodes_list.listbox.insert(tk.END, name)
self.reset_values()
@ -232,6 +238,12 @@ class CustomNodesDialog(Dialog):
node_draw.image_file = Path(self.image_file).stem
node_draw.image = self.image
node_draw.services = self.services
logging.debug(
"edit custom node (%s), image: (%s), services (%s)",
name,
self.image_file,
self.services,
)
self.app.core.custom_nodes[name] = node_draw
self.nodes_list.listbox.delete(self.selected_index)
self.nodes_list.listbox.insert(self.selected_index, name)

View file

@ -1,7 +1,6 @@
"""
emane configuration
"""
import logging
import tkinter as tk
import webbrowser
from tkinter import ttk
@ -223,7 +222,6 @@ class EmaneConfigDialog(Dialog):
draw emane model configuration
"""
model_name = self.emane_model.get()
logging.info("configuring emane model: %s", model_name)
dialog = EmaneModelDialog(
self, self.app, self.canvas_node.core_node, model_name
)

View file

@ -1,7 +1,6 @@
"""
link configuration
"""
import logging
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING, Union
@ -248,7 +247,6 @@ class LinkConfigurationDialog(Dialog):
self.color_button.config(background=color)
def click_apply(self):
logging.debug("click apply")
self.app.canvas.itemconfigure(self.edge.id, width=self.width.get())
self.app.canvas.itemconfigure(self.edge.id, fill=self.color.get())
link = self.edge.link
@ -324,8 +322,6 @@ class LinkConfigurationDialog(Dialog):
self.destroy()
def change_symmetry(self):
logging.debug("change symmetry")
if self.is_symmetric:
self.is_symmetric = False
self.symmetry_var.set("<<")

View file

@ -25,6 +25,7 @@ class CanvasWirelessEdge:
dst: int,
canvas: "CanvasGraph",
):
logging.debug("Draw wireless link from node %s to node %s", src, dst)
self.token = token
self.src = src
self.dst = dst
@ -167,6 +168,7 @@ class CanvasEdge:
self.check_wireless()
self.canvas.tag_raise(self.src)
self.canvas.tag_raise(self.dst)
logging.debug("Draw wired link from node %s to node %s", self.src, dst)
def is_wireless(self) -> [bool, bool]:
src_node = self.canvas.nodes[self.src]
@ -198,6 +200,7 @@ class CanvasEdge:
src_node.add_antenna()
def delete(self):
logging.debug("Delete canvas edge, id: %s", self.id)
self.canvas.delete(self.id)
if self.link:
self.canvas.delete(self.text_src)
@ -210,7 +213,6 @@ class CanvasEdge:
self.canvas.itemconfig(self.id, fill=EDGE_COLOR, width=EDGE_WIDTH)
def create_context(self, event: tk.Event):
logging.debug("create link context")
context = tk.Menu(self.canvas)
themes.style_menu(context)
context.add_command(label="Configure", command=self.configure)
@ -224,6 +226,5 @@ class CanvasEdge:
context.post(event.x_root, event.y_root)
def configure(self):
logging.debug("link configuration")
dialog = LinkConfigurationDialog(self.canvas, self.canvas.app, self)
dialog.show()

View file

@ -216,6 +216,7 @@ class CanvasGraph(tk.Canvas):
"""
# draw existing nodes
for core_node in session.nodes:
logging.debug("drawing node %s", core_node)
# peer to peer node is not drawn on the GUI
if NodeUtils.is_ignore_node(core_node.type):
continue
@ -231,7 +232,7 @@ class CanvasGraph(tk.Canvas):
# draw existing links
for link in session.links:
logging.info("drawing link: %s", link)
logging.debug("drawing link: %s", link)
canvas_node_one = self.core.canvas_nodes[link.node_one_id]
node_one = canvas_node_one.core_node
canvas_node_two = self.core.canvas_nodes[link.node_two_id]
@ -388,7 +389,6 @@ class CanvasGraph(tk.Canvas):
# set dst node and snap edge to center
edge.complete(self.selected)
logging.debug("drawing edge token: %s", edge.token)
self.edges[edge.token] = edge
node_src = self.nodes[edge.src]
@ -508,7 +508,7 @@ class CanvasGraph(tk.Canvas):
logging.debug("click press(%s): %s", self.cursor, selected)
x_check = self.cursor[0] - self.offset[0]
y_check = self.cursor[1] - self.offset[1]
logging.debug("clock press ofset(%s, %s)", x_check, y_check)
logging.debug("click press offset(%s, %s)", x_check, y_check)
is_node = selected in self.nodes
if self.mode == GraphMode.EDGE and is_node:
x, y = self.coords(selected)
@ -544,13 +544,13 @@ class CanvasGraph(tk.Canvas):
node = self.nodes[selected]
self.select_object(node.id)
self.selected = selected
logging.info(
"selected coords: (%s, %s)",
logging.debug(
"selected node(%s), coords: (%s, %s)",
node.core_node.name,
node.core_node.position.x,
node.core_node.position.y,
)
else:
logging.debug("create selection box")
if self.mode == GraphMode.SELECT:
shape = Shape(self.app, self, ShapeType.RECTANGLE, x, y)
self.select_box = shape
@ -631,7 +631,6 @@ class CanvasGraph(tk.Canvas):
self.select_box.shape_motion(x, y)
def click_context(self, event: tk.Event):
logging.info("context event: %s", self.context)
if not self.context:
selected = self.get_selected(event)
canvas_node = self.nodes.get(selected)
@ -796,14 +795,14 @@ class CanvasGraph(tk.Canvas):
self.tag_raise(component)
def update_grid(self):
logging.info("updating grid show: %s", self.show_grid.get())
logging.debug("updating grid show grid: %s", self.show_grid.get())
if self.show_grid.get():
self.itemconfig(tags.GRIDLINE, state=tk.NORMAL)
else:
self.itemconfig(tags.GRIDLINE, state=tk.HIDDEN)
def set_wallpaper(self, filename: str):
logging.info("setting wallpaper: %s", filename)
logging.debug("setting wallpaper: %s", filename)
if filename:
img = Image.open(filename)
self.wallpaper = img
@ -835,14 +834,10 @@ class CanvasGraph(tk.Canvas):
def copy(self):
if self.selection:
logging.debug(
"store current selection to to_copy, number of nodes: %s",
len(self.selection),
)
logging.debug("to copy %s nodes", len(self.selection))
self.to_copy = self.selection.keys()
def paste(self):
logging.debug("copy")
# maps original node canvas id to copy node canvas id
copy_map = {}
# the edges that will be copy over

View file

@ -1,3 +1,4 @@
import logging
import tkinter as tk
from tkinter import font
from typing import TYPE_CHECKING
@ -64,9 +65,10 @@ class CanvasNode:
self.canvas.tag_bind(self.id, "<Leave>", self.on_leave)
def delete(self):
logging.debug("Delete canvas node for %s", self.core_node)
self.canvas.delete(self.id)
self.canvas.delete(self.text_id)
self.delete_antennae()
self.delete_antennas()
def add_antenna(self):
x, y = self.canvas.coords(self.id)
@ -84,14 +86,16 @@ class CanvasNode:
"""
delete one antenna
"""
logging.debug("Delete an antenna on %s", self.core_node.name)
if self.antennae:
antenna_id = self.antennae.pop()
self.canvas.delete(antenna_id)
def delete_antennae(self):
def delete_antennas(self):
"""
delete all antennas
"""
logging.debug("Remove all antennas for %s", self.core_node.name)
for antenna_id in self.antennae:
self.canvas.delete(antenna_id)
self.antennae.clear()

View file

@ -163,6 +163,7 @@ class Shape:
self.canvas.move(self.text_id, x_offset, y_offset)
def delete(self):
logging.debug("Delete shape, id(%s)", self.id)
self.canvas.delete(self.id)
self.canvas.delete(self.text_id)

View file

@ -33,9 +33,11 @@ class MenuAction:
self.canvas = app.canvas
def cleanup_old_session(self, session_id):
logging.info("cleaning up old session")
self.app.core.stop_session()
response = self.app.core.stop_session()
self.app.core.delete_session(session_id)
logging.info(
"Stop session(%s) and delete it, result: %s", session_id, response.result
)
def prompt_save_running_session(self, quitapp: bool = False):
"""
@ -66,7 +68,6 @@ class MenuAction:
self.prompt_save_running_session(quitapp=True)
def file_save_as_xml(self, event: tk.Event = None):
logging.info("menuaction.py file_save_as_xml()")
init_dir = self.app.core.xml_dir
if not init_dir:
init_dir = str(XMLS_PATH)
@ -83,14 +84,12 @@ class MenuAction:
init_dir = self.app.core.xml_dir
if not init_dir:
init_dir = str(XMLS_PATH)
logging.info("menuaction.py file_open_xml()")
file_path = filedialog.askopenfilename(
initialdir=init_dir,
title="Open",
filetypes=(("XML Files", "*.xml"), ("All Files", "*")),
)
if file_path:
logging.info("opening xml: %s", file_path)
self.app.core.xml_file = file_path
self.app.core.xml_dir = str(os.path.dirname(file_path))
self.prompt_save_running_session()
@ -117,22 +116,22 @@ class MenuAction:
webbrowser.open_new("http://coreemu.github.io/core/")
def session_options(self):
logging.debug("Click session options")
logging.debug("Click options")
dialog = SessionOptionsDialog(self.app, self.app)
dialog.show()
def session_change_sessions(self):
logging.debug("Click session change sessions")
logging.debug("Click change sessions")
dialog = SessionsDialog(self.app, self.app)
dialog.show()
def session_hooks(self):
logging.debug("Click session hooks")
logging.debug("Click hooks")
dialog = HooksDialog(self.app, self.app)
dialog.show()
def session_servers(self):
logging.debug("Click session emulation servers")
logging.debug("Click emulation servers")
dialog = ServersDialog(self.app, self.app)
dialog.show()
@ -151,14 +150,11 @@ class MenuAction:
self.app.core.cancel_throughputs()
def copy(self, event: tk.Event = None):
logging.debug("copy")
self.app.canvas.copy()
def paste(self, event: tk.Event = None):
logging.debug("paste")
self.app.canvas.paste()
def config_throughput(self):
logging.debug("not implemented")
dialog = ThroughputDialog(self.app, self.app)
dialog.show()

View file

@ -105,7 +105,7 @@ class Toolbar(ttk.Frame):
self.create_annotation_button()
def design_select(self, button: ttk.Button):
logging.info("selecting design button: %s", button)
logging.debug("selecting design button: %s", button)
self.select_button.state(["!pressed"])
self.link_button.state(["!pressed"])
self.node_button.state(["!pressed"])
@ -114,7 +114,7 @@ class Toolbar(ttk.Frame):
button.state(["pressed"])
def runtime_select(self, button: ttk.Button):
logging.info("selecting runtime button: %s", button)
logging.debug("selecting runtime button: %s", button)
self.runtime_select_button.state(["!pressed"])
self.stop_button.state(["!pressed"])
self.plot_button.state(["!pressed"])
@ -285,7 +285,7 @@ class Toolbar(ttk.Frame):
dialog.show()
def update_button(self, button: ttk.Button, image: "ImageTk", node_draw: NodeDraw):
logging.info("update button(%s): %s", button, node_draw)
logging.debug("update button(%s): %s", button, node_draw)
self.hide_pickers()
button.configure(image=image)
button.image = image
@ -293,7 +293,7 @@ class Toolbar(ttk.Frame):
self.app.canvas.node_draw = node_draw
def hide_pickers(self):
logging.info("hiding pickers")
logging.debug("hiding pickers")
if self.node_picker:
self.node_picker.destroy()
self.node_picker = None
@ -417,6 +417,7 @@ class Toolbar(ttk.Frame):
"""
redraw buttons on the toolbar, send node and link messages to grpc server
"""
logging.info("Click stop button")
self.app.canvas.hide_context()
self.app.statusbar.progress_bar.start(5)
self.time = time.perf_counter()
@ -434,7 +435,7 @@ class Toolbar(ttk.Frame):
messagebox.showerror("Stop Error", "Errors stopping session")
def update_annotation(self, image: "ImageTk.PhotoImage", shape_type: ShapeType):
logging.info("clicked annotation: ")
logging.debug("clicked annotation: ")
self.hide_pickers()
self.annotation_button.configure(image=image)
self.annotation_button.image = image

View file

@ -31,7 +31,8 @@ class InputValidation:
self.rgb = self.master.register(self.check_rbg)
self.hex = self.master.register(self.check_hex)
def ip_focus_out(self, event: tk.Event):
@classmethod
def ip_focus_out(cls, event: tk.Event):
value = event.widget.get()
try:
IPNetwork(value)
@ -39,12 +40,14 @@ class InputValidation:
event.widget.delete(0, tk.END)
event.widget.insert(tk.END, "invalid")
def focus_out(self, event: tk.Event, default: str):
@classmethod
def focus_out(cls, event: tk.Event, default: str):
value = event.widget.get()
if value == "":
event.widget.insert(tk.END, default)
def check_positive_int(self, s: str) -> bool:
@classmethod
def check_positive_int(cls, s: str) -> bool:
if len(s) == 0:
return True
try:
@ -55,7 +58,8 @@ class InputValidation:
except ValueError:
return False
def check_positive_float(self, s: str) -> bool:
@classmethod
def check_positive_float(cls, s: str) -> bool:
if len(s) == 0:
return True
try:
@ -66,7 +70,8 @@ class InputValidation:
except ValueError:
return False
def check_node_name(self, s: str) -> bool:
@classmethod
def check_node_name(cls, s: str) -> bool:
if len(s) < 0:
return False
if len(s) == 0:
@ -76,7 +81,8 @@ class InputValidation:
return False
return True
def check_canvas_int(self, s: str) -> bool:
@classmethod
def check_canvas_int(cls, s: str) -> bool:
if len(s) == 0:
return True
try:
@ -87,7 +93,8 @@ class InputValidation:
except ValueError:
return False
def check_canvas_float(self, s: str) -> bool:
@classmethod
def check_canvas_float(cls, s: str) -> bool:
if not s:
return True
try:
@ -98,7 +105,8 @@ class InputValidation:
except ValueError:
return False
def check_ip4(self, s: str) -> bool:
@classmethod
def check_ip4(cls, s: str) -> bool:
if not s:
return True
pat = re.compile("^([0-9]+[.])*[0-9]*$")
@ -117,7 +125,8 @@ class InputValidation:
else:
return False
def check_rbg(self, s: str) -> bool:
@classmethod
def check_rbg(cls, s: str) -> bool:
if not s:
return True
if s.startswith("0") and len(s) >= 2:
@ -131,7 +140,8 @@ class InputValidation:
except ValueError:
return False
def check_hex(self, s: str) -> bool:
@classmethod
def check_hex(cls, s: str) -> bool:
if not s:
return True
pat = re.compile("^([#]([0-9]|[a-f])+)$|^[#]$")