Reference
bombard
Modules
bombard.args
Parse bombard command line args.
Attributes
bombard.args.CAMPAIGN_FILE_NAME
module-attribute
CAMPAIGN_FILE_NAME = 'bombard.yaml'
bombard.args.DIR_DESC_FILE_NAME
module-attribute
DIR_DESC_FILE_NAME = 'README.md'
bombard.args.EXAMPLES_PREFIX
module-attribute
EXAMPLES_PREFIX = 'bombard://'
bombard.args.INIT_EXAMPLE
module-attribute
INIT_EXAMPLE = 'easy.yaml'
bombard.args.REPEAT
module-attribute
REPEAT = 10
bombard.args.THREADS_NUM
module-attribute
THREADS_NUM = 10
bombard.args.THRESHOLD
module-attribute
THRESHOLD = 1000
bombard.args.TIMEOUT
module-attribute
TIMEOUT = 15
Functions
bombard.args.get_args
get_args() -> Any
bombard.attr_dict
Dict with access to keys as attributes.
d = AttrDict({'a': 'b', 'c': 'g'}) d.c 'g'
Instance can be used as callable to set value to the dict
d = AttrDict({}) d(k='t') d.k 't'
(!) All values that you set will be repeated to master
that you used to init the object.
master = {'j': 'l'} d = AttrDict(master) d(h='q') master['h'] 'q' d['w'] = 'e' master['w'] 'e'
And it works without master
d = AttrDict() d(f=7) d.f 7
Classes
bombard.attr_dict.AttrDict
Bases: dict[str, Any]
You can access all dict values as attributes. All changes immediately repeated in master_dict.
bombard.attr_dict.AttrDict.master_dict
instance-attribute
master_dict = master_dict
bombard.bombardier
Attributes
bombard.bombardier.AMMO
module-attribute
AMMO = 'ammo'
bombard.bombardier.DEFAULT_OK
module-attribute
DEFAULT_OK = {200}
bombard.bombardier.DEFAULT_OVERLOAD
module-attribute
DEFAULT_OVERLOAD = [502, 504]
bombard.bombardier.PREPARE
module-attribute
PREPARE = 'prepare'
bombard.bombardier.log
module-attribute
log = getLogger()
Classes
bombard.bombardier.Bombardier
Bases: WeaverMill
Use horde of threads to make HTTP-requests
bombard.bombardier.Bombardier.args
instance-attribute
args = args
bombard.bombardier.Bombardier.campaign
instance-attribute
campaign = campaign_book
bombard.bombardier.Bombardier.ok
instance-attribute
ok = ok_statuses if ok_statuses is not None else DEFAULT_OK
bombard.bombardier.Bombardier.overload
instance-attribute
overload = (
overload_statuses
if overload_statuses is not None
else DEFAULT_OVERLOAD
)
bombard.bombardier.Bombardier.reporter
instance-attribute
reporter = Reporter(
time_units="ms" if ms else None,
time_threshold_ms=int(threshold),
success_statuses=ok,
)
bombard.bombardier.Bombardier.request_fired
instance-attribute
request_fired = False
bombard.bombardier.Bombardier.resp_count
instance-attribute
resp_count = 0
bombard.bombardier.Bombardier.show_request
instance-attribute
show_request = {1: 'Sent 1st request..'}
bombard.bombardier.Bombardier.show_response
instance-attribute
show_response = {1: 'Got 1st response..'}
bombard.bombardier.Bombardier.supply
instance-attribute
supply = supply if supply is not None else {}
bombard.bombardier.Bombardier.beautify_url
staticmethod
beautify_url(
url: str, method: str, body: Optional[str]
) -> str
bombard.bombardier.Bombardier.get_headers
staticmethod
get_headers(
request: dict[str, Any], body_is_json: bool
) -> dict[str, Any]
Treat special value 'json' as Content-Type: application/json
bombard.bombardier.Bombardier.process_resp
process_resp(
ammo: dict[str, Any],
status: Union[int, str],
resp: str,
elapsed: int,
size: int,
) -> None
bombard.bombardier.Bombardier.reload
reload(
requests: Any,
repeat: Optional[int] = None,
prepare: bool = False,
**kwargs: Any
) -> None
Add request(s) to the bombardier queue repeat
-times (args.repeat if None).
If repeat
field exists in the request additionally repeats as defined by it.
Requests can be one request or list of requests. If supply specified it'll be used in addition to self.supply.
Arg prepare
indicate call from main, not from request script.
So we know if any scripts call reload (self.request_fired)
bombard.bombardier.Bombardier.report
report() -> None
bombard.bombardier.Bombardier.status_coloured
status_coloured(status: Union[str, int]) -> str
Paint ok status as green and overload as red using terminal control codes.
If status has special string value (EXCEPTION_STATUS) paint it red.
bombard.bombardier.Bombardier.worker
worker(thread_id: int, job: dict[str, Any]) -> None
Thread callable. Strike ammo from queue.
Functions
bombard.bombardier.apply_supply
apply_supply(s: str, supply: dict[str, Any]) -> str
bombard.bombardier.apply_supply_dict
apply_supply_dict(
request: dict[str, Any], supply: dict[str, Any]
) -> dict[str, Any]
Use supply to substitute all {name} in request strings.
Modules
bombard.campaign_yaml
Bombard campaign loader.
Extends yaml loader with loading external files !include file.ext
.
Excludes lines that import mock_globals.
Attributes
bombard.campaign_yaml.SIGNATURE
module-attribute
SIGNATURE = 'bombard.mock_globals'
Classes
bombard.campaign_yaml.IncludesLoader
Bases: SafeLoader
bombard.campaign_yaml.IncludesLoader.include
include(node)
bombard.campaign_yaml.IncludesLoader.wrap_in_yaml_document
staticmethod
wrap_in_yaml_document(msg: str) -> str
Converts multi-line msg to yaml document that we can insert into yaml
bombard.campaign_yaml.Yaml
bombard.campaign_yaml.Yaml.full_load
staticmethod
full_load(stream: Any, Loader: Any = None) -> Any
Mimics yaml interface for seamless injection
bombard.campaign_yaml.Yaml.load
staticmethod
load(stream: Any, Loader: Any = None) -> Any
Mimics yaml interface for seamless injection
bombard.expand_file_name
Attributes
Functions
bombard.expand_file_name.expand_relative_file_name
expand_relative_file_name(file_name: str) -> str
Replace RELATIVE_PREFIX with package folder.
so bombard script can use internal examples without full path spec
bombard.expand_file_name.get_campaign_file_name
get_campaign_file_name(args: Any) -> str
bombard.expand_file_name.show_folder
show_folder(folder_path: str) -> None
bombard.http_request
Attributes
bombard.http_request.EXCEPTION_STATUS
module-attribute
EXCEPTION_STATUS = '!!!'
Functions
bombard.http_request.http_request
http_request(
url: str,
method: str = "GET",
headers: Optional[dict[str, Any]] = None,
body: Optional[str] = None,
timeout: Optional[int] = None,
) -> tuple[Union[int, str], Any]
Make HTTP request.
Returns tuple:
bombard.main
Bombard's main
Attributes
Classes
Functions
bombard.main.add_names_to_requests
add_names_to_requests(
campaign_book: dict[str, Any],
) -> None
Duplicate names inside requests so worker will see it and use in stat report
bombard.main.campaign
campaign(args: Any) -> None
bombard.main.get_supply_from_cli
get_supply_from_cli(
supply: Optional[list[str]],
) -> dict[str, Any]
Extract key=value pairs from list of supply
args
bombard.main.guess_type
guess_type(value: str) -> Union[str, int, float]
Converts value in int or float if possible
bombard.main.init
init(args: Any) -> None
Copies the example to current folder as bombard.yaml
bombard.main.load_book_supply
load_book_supply(
cli_supply: dict[str, Any], book_supply: dict[str, Any]
) -> None
Updates CLI supply with supply from campaign book. But do not overwrite CLI supply with book supply - values from CLI have bigger priority.
bombard.main.main
main() -> None
bombard.main.start_campaign
start_campaign(
args: Any, campaign_book: dict[str, Any]
) -> None
bombard.mock_globals
If you use include files for scripts add into them
from bombard.mock_globals import *; master(<your yaml>)
That defines globals so you have valid code and code autocomplete in your IDE editor.
All strings with bombard.examples.mock_globals
will be automatically removed before running bombard scripts.
Attributes
bombard.mock_globals.ammo
module-attribute
ammo = _Ammo()
bombard.mock_globals.args
module-attribute
args = _Args()
bombard.mock_globals.resp
module-attribute
resp = {}
bombard.mock_globals.supply
module-attribute
supply = _Supply()
Functions
bombard.mock_globals.master
master(yaml_file_name: str)
Add names from the yaml file to the unit globals. That makes code autocomplete work in bombard script.
bombard.mock_globals.reload
reload(requests, repeat=None, **kwargs)
bombard.pretty_ns
- time_ns for Python before 3.7
- Elapsed context manager
- pretty_ns to represent time elapsed in human lovable form
Usage:
start = time_ns() import time time.sleep(0.0000001) pretty_ns((start + 100) - start) '0.1 mks'
with Timer() as timer: ... import time ... time.sleep(0.0000001) ... timer.pretty.endswith('s') ... timer.ns > 0 True True
Attributes
bombard.pretty_ns.time_ns
module-attribute
time_ns = time_ns
Classes
bombard.pretty_ns.Timer
bombard.pretty_ns.Timer.ns
property
ns: int
bombard.pretty_ns.Timer.pretty
property
pretty: str
bombard.pretty_ns.Timer.start
instance-attribute
start: int
Functions
bombard.pretty_ns.emul_time_ns
emul_time_ns() -> int
bombard.pretty_ns.pretty_ns
pretty_ns(
elapsed_ns: int, fixed_units: Optional[str] = None
) -> str
for earlier Python versions this is emulation of the Python3.7 time_ns
bombard.pretty_sz
Functions
bombard.pretty_sz.pretty_sz
pretty_sz(size: Union[int, float]) -> str
bombard.report
Bombard reporter.
Use:
* log
to add each request result.
* report
to generate report.
Attributes
bombard.report.ARRAY_UINT32
module-attribute
ARRAY_UINT32 = 'L'
bombard.report.ARRAY_UINT64
module-attribute
ARRAY_UINT64 = 'Q'
bombard.report.FAIL_GROUP
module-attribute
FAIL_GROUP = 'fail'
bombard.report.SIZE
module-attribute
SIZE = 'size'
bombard.report.SUCCESS_GROUP
module-attribute
SUCCESS_GROUP = 'success'
bombard.report.TIME
module-attribute
TIME = 'time'
bombard.report.TIME_DENOMINATOR
module-attribute
TIME_DENOMINATOR = 1
Classes
bombard.report.Reporter
Report bombard's result
Access to stat data only by log(), filter(), reduce()
bombard.report.Reporter.DIMENSIONS
instance-attribute
DIMENSIONS: dict[str, dict[str, Any]] = {
TIME: {
"type": ARRAY_UINT64,
"pretty_func": pretty_time,
},
SIZE: {"type": ARRAY_UINT32, "pretty_func": pretty_sz},
}
bombard.report.Reporter.STAT_DEFAULT
instance-attribute
STAT_DEFAULT: dict[str, MutableSequence[Any]] = {
name: array(params["type"])
for (name, params) in items()
}
bombard.report.Reporter.ok
instance-attribute
ok = success_statuses or {200}
bombard.report.Reporter.start_ns
instance-attribute
start_ns = time_ns()
bombard.report.Reporter.stat
instance-attribute
stat: dict[
str, dict[int, dict[str, MutableSequence[Any]]]
] = {}
bombard.report.Reporter.time_threshold_ns
instance-attribute
time_threshold_ns = time_threshold_ms * 10 ** 6
bombard.report.Reporter.time_units
instance-attribute
time_units = time_units
bombard.report.Reporter.total_elapsed_ns
property
total_elapsed_ns: int
bombard.report.Reporter.dimension_stat_report
staticmethod
dimension_stat_report(
dimension_values: MutableSequence[Union[int, float]],
pretty_func: Callable[[Union[int, float]], str],
) -> str
bombard.report.Reporter.filter
filter(
dimension_name: str,
status_group_filter: Optional[str] = None,
request_name_filter: Optional[str] = None,
) -> MutableSequence[Any]
Filter the dimension by group and/or request_name, Returns array with values from dimention_name
bombard.report.Reporter.filtered_report
filtered_report(
status_group_filter: Optional[str] = None,
request_name_filter: Optional[str] = None,
) -> str
Filter by group and/or request_name. Returns report str with stats for all dimensions.
bombard.report.Reporter.group_name_by_status
group_name_by_status(status: int) -> str
All this statuses should be in GROUPS
bombard.report.Reporter.log
log(
status: Union[int, str],
elapsed: int,
request_name: str,
response_size: int,
) -> None
Add result to the report
:param status: HTTP response status :param elapsed: Request-Response time, ns :param request_name: Request name or None :param response_size: Response body size
bombard.report.Reporter.pretty_ns
pretty_ns(elapsed_ns: int, paint: bool = True) -> str
bombard.report.Reporter.pretty_time
pretty_time(elapsed: int, paint: bool = True) -> str
bombard.report.Reporter.reduce
reduce(
reduce_func: Callable[[Any], Any],
dimension_name: str,
status_group_filter: Optional[str] = None,
request_name_filter: Optional[str] = None,
) -> int
Reduce the dimension by group and/or request_name with the reduce_func Returns dict {'time':, 'size':}
bombard.report.Reporter.report
report() -> str
bombard.report.Reporter.statuses_report
statuses_report(
request_name_filter: Optional[str] = None,
) -> str
Functions
Modules
bombard.request_logging
Logger with thread storage to add to log information about request.
Use main_thread/sending/receiving to switch logger modes.
Attributes
bombard.request_logging.log
module-attribute
log = getLogger()
bombard.request_logging.thread_data
module-attribute
thread_data = local()
Classes
bombard.request_logging.RequestFormatter
Bases: Formatter
bombard.request_logging.RequestFormatter.format
format(record: Any) -> str
Functions
bombard.request_logging.main_thread
main_thread() -> None
We are in main thread
bombard.request_logging.receiving
receiving() -> None
Got response to request
bombard.request_logging.sending
sending(
thread_id: int, request_id: str, request_name: str
) -> None
Start sending request
bombard.request_logging.setup_logging
setup_logging(
level: int, log_file_name: Optional[str] = None
) -> None
bombard.show_descr
Functions
bombard.show_descr.markdown_for_terminal
markdown_for_terminal(descr: str) -> str
bombard.terminal_colours
Colourize text in terminal source https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
You can use it function style
green('Hello!') '\x1b[1;32mHello!\x1b[0m'
Or include style
f'{YELLOW}Hello{OFF}, {RED}world{OFF}!' '\x1b[1;33mHello\x1b[0m, \x1b[1;31mworld\x1b[0m!'
Under the hood this is colorama. But I keep my wrapper in this module as legacy.
Attributes
bombard.terminal_colours.BROWN
module-attribute
BROWN = code_to_chars(f'{DIM};{YELLOW}')
bombard.terminal_colours.DARK_RED
module-attribute
DARK_RED = code_to_chars(f'{DIM};{RED}')
bombard.terminal_colours.GRAY
module-attribute
GRAY = code_to_chars(f'{BRIGHT};{BLACK}')
bombard.terminal_colours.GREEN
module-attribute
GREEN = code_to_chars(f'{BRIGHT};{GREEN}')
bombard.terminal_colours.OFF
module-attribute
OFF = code_to_chars(RESET_ALL)
bombard.terminal_colours.RED
module-attribute
RED = code_to_chars(f'{BRIGHT};{RED}')
bombard.terminal_colours.YELLOW
module-attribute
YELLOW = code_to_chars(f'{BRIGHT};{YELLOW}')
Functions
bombard.terminal_colours.brown
brown(s: str) -> str
bombard.terminal_colours.dark_red
dark_red(s: str) -> str
bombard.terminal_colours.gray
gray(s: str) -> str
bombard.terminal_colours.green
green(s: str) -> str
bombard.terminal_colours.paint_it
paint_it(msg: str, colour: str) -> str
bombard.terminal_colours.red
red(s: str) -> str
bombard.terminal_colours.yellow
yellow(s: str) -> str
bombard.version
Attributes
bombard.version.VERSION
module-attribute
VERSION = '1.20.5'
bombard.weaver_mill
Multithreading jobs processor abstraction.
Override method worker in descendant to do a job.
Add jobs with put
.
Start processing with start
.
Classes
bombard.weaver_mill.WeaverMill
bombard.weaver_mill.WeaverMill.job_count
instance-attribute
job_count = 0
bombard.weaver_mill.WeaverMill.queue
instance-attribute
queue: Queue[Optional[dict[str, Any]]] = Queue()
bombard.weaver_mill.WeaverMill.threads
instance-attribute
threads = []
bombard.weaver_mill.WeaverMill.threads_num
instance-attribute
threads_num = threads_num
bombard.weaver_mill.WeaverMill.process
process() -> None
Starts all threads and lock until queue is empty
bombard.weaver_mill.WeaverMill.put
put(job: dict[str, Any]) -> None
Add job to queue.
To start processing use process
.
bombard.weaver_mill.WeaverMill.stop
stop() -> None
Stops all threads - send stop signal to queue and lock until they stop
bombard.weaver_mill.WeaverMill.thread_worker
thread_worker(thread_id: int) -> None
Get job from queue and pass it to abstract worker that should be implemented in descendant.
Even if worker throw exception we mark the job we gave him as done.
Job == None is a signal to stop work.
bombard.weaver_mill.WeaverMill.worker
abstractmethod
worker(thread_id: int, job: dict[str, Any]) -> None
Implement your job processor, runs in thread.
:param thread_id: just sequential number of the thread we work into :param job: job from queue