Skip to content

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.

Attributes
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

Attributes
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 {}
Functions
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'
bombard.campaign_yaml.yaml module-attribute
yaml = Yaml()
Classes
bombard.campaign_yaml.IncludesLoader

Bases: SafeLoader

Functions
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
Functions
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
Attributes
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.GROUPS module-attribute
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()

Attributes
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
Functions
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.pretty_ns module-attribute
pretty_ns: Callable[[int], str] = pretty_ns
bombard.request_logging.thread_data module-attribute
thread_data = local()
Classes
bombard.request_logging.RequestFormatter

Bases: Formatter

Functions
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
Attributes
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
Functions
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