Commit 95335032 authored by David Hendriks's avatar David Hendriks
Browse files

Updating the docstrings to contain all the necessary types, arguments and returns

parent 6da09a03
Loading
Loading
Loading
Loading
+101 −27
Original line number Diff line number Diff line
@@ -9,17 +9,18 @@ import subprocess
import socket
import ctypes
import uuid

from typing import Union, Tuple, Optional
from binarycpython.utils.functions import temp_dir, remove_file


def autogen_C_logging_code(logging_dict, verbose=0):
def autogen_C_logging_code(logging_dict: dict, verbose: int=0) -> Optional[str]:
    """
    Function that autogenerates PRINTF statements for binaryc.
    Input is a dictionary where the key is the header of that logging line
    and items which are lists of parameters that will be put in that logging line

    Example::
    Example:
        input dictionary should look like this::

            {'MY_STELLAR_DATA':
                [
                    'model.time',
@@ -29,6 +30,12 @@ def autogen_C_logging_code(logging_dict, verbose=0):
                ]
            }

    Args:
        logging_dict: Dictionary containing lists of parameters that binary_c has to output. The keys are used by binary_c as start of the sentence. 
        verbose: Level of verbosity. Defaults to zero if not set explicilty.

    Returns:
        string containing C printf statement built to output the parameters given as input.
    """

    # Check if the input is of the correct form
@@ -37,6 +44,7 @@ def autogen_C_logging_code(logging_dict, verbose=0):
        return None

    code = ""

    # Loop over dict keys
    for key in logging_dict:
        if verbose > 0:
@@ -71,9 +79,43 @@ def autogen_C_logging_code(logging_dict, verbose=0):


####################################################################################
def binary_c_log_code(code, verbose=0):
def binary_c_log_code(code: str, verbose: int=0) -> str:
    """
    Function to construct the code to construct the custom logging function
    
    Example:
        Code to log and terminate evolution when the primary star becomes a NS

        ::
        if(stardata->star[0].stellar_type>=NS)
        {
            if (stardata->model.time < stardata->model.max_evolution_time)
            {
                Printf("EXAMPLE_LOG_CO %30.12e %g %g %g %g %d %d\\n",
                    // 
                    stardata->model.time, // 1

                    stardata->star[0].mass, //2
                    stardata->previous_stardata->star[0].mass, //3

                    stardata->star[0].radius, //4
                    stardata->previous_stardata->star[0].radius, //5

                    stardata->star[0].stellar_type, //6
                    stardata->previous_stardata->star[0].stellar_type //7
              );
            };
            /* Kill the simulation to save time */
            stardata->model.max_evolution_time = stardata->model.time - stardata->model.dtm;
        };

    Args:
        code: Exact c-statement to output information in binary_c. Can be wrapped in logical statements. 
        verbose: Level of verbosity. Defaults to zero if not set explicilty.

    Returns:
        string containing the custom logging code. This includes all the includes and other definitions. This code will be used as the shared library

    """

    if verbose > 0:
@@ -108,11 +150,17 @@ void binary_c_API_function custom_output_function(struct stardata_t * stardata)
    return textwrap.dedent(custom_logging_function_string)


def binary_c_write_log_code(code, filename, verbose=0):
def binary_c_write_log_code(code: str, filename: str, verbose: int=0) -> None:
    """
    Function to write the generated logging code to a file

    Args:
        code: string containing the custom logging code to write to a file. 
        filename: target filename. 
        verbose: Level of verbosity. Defaults to zero if not set explicilty.
    """

    # TODO: change this. I don't like the cwd
    cwd = os.getcwd()
    filePath = os.path.join(cwd, filename)

@@ -127,9 +175,16 @@ def binary_c_write_log_code(code, filename, verbose=0):
        file.write(code)


def from_binary_c_config(config_file, flag):
def from_binary_c_config(config_file: str, flag: str) -> str:
    """
    Function to run the binaryc_config command with flags

    Args:
        config_file: binary_c-config filepath TODO: change the name of this
        flag: flag used in the binary_c-config call. 

    Returns:
        returns the result of <binary_c-config> <flag>
    """

    res = subprocess.check_output(
@@ -139,22 +194,25 @@ def from_binary_c_config(config_file, flag):
    )

    # convert and chop off newline
    res = res.decode("utf").rstrip()
    res = res.decode("utf-8").rstrip()

    return res


def return_compilation_dict(verbose=0):
def return_compilation_dict(verbose: int=0) -> dict:
    """
    Function to build the compile command for the shared library

    inspired by binary_c_inline_config command in perl
    Inspired by binary_c_inline_config command in perl

    TODO: this function still has some cleaning up to do wrt default values for the compile command
    # https://developers.redhat.com/blog/2018/03/21/compiler-and-linker-flags-gcc/

    Args:
        verbose: Level of verbosity. Defaults to zero if not set explicilty.

    returns:
     - string containing the command to build the shared library
    Returns:
        string containing the command to build the shared library
    """

    if verbose > 0:
@@ -166,7 +224,6 @@ def return_compilation_dict(verbose=0):
    if BINARY_C_DIR:
        BINARY_C_CONFIG = os.path.join(BINARY_C_DIR, "binary_c-config")
        BINARY_C_SRC_DIR = os.path.join(BINARY_C_DIR, "src")
        # TODO: build in check to see whether the file exists
    else:
        raise NameError("Envvar BINARY_C doesnt exist")

@@ -240,10 +297,18 @@ def return_compilation_dict(verbose=0):
    return {"cc": cc, "ld": ld, "ccflags": ccflags, "libs": libs, "inc": inc}


def compile_shared_lib(code, sourcefile_name, outfile_name, verbose=0):
def compile_shared_lib(code: str, sourcefile_name: str, outfile_name: str, verbose: int=0) -> None:
    """
    Function to write the custom logging code to a file and then compile it.

    TODO: nicely put in the -fPIC
    TODO: consider returning a status

    Args:
        code: string containing the custom logging code
        sourcefile_name: name of the file that will contain the code
        outfile_name: name of the file that will be the shared library
        verbose: Level of verbosity. Defaults to zero if not set explicilty.
    """

    # Write code to file
@@ -272,8 +337,6 @@ def compile_shared_lib(code, sourcefile_name, outfile_name, verbose=0):

    # Execute compilation and create the library
    if verbose > 0:
        # BINARY_C_DIR = os.getenv("BINARY_C")
        # BINARY_C_SRC_DIR = os.path.join(BINARY_C_DIR, "src")
        print(
            "Building shared library for custom logging with (binary_c.h) on {}\n".format(
                socket.gethostname()
@@ -291,12 +354,19 @@ def compile_shared_lib(code, sourcefile_name, outfile_name, verbose=0):
            print("Output of compilation command:\n{}".format(res))


def create_and_load_logging_function(custom_logging_code, verbose=0):
def create_and_load_logging_function(custom_logging_code: str, verbose: int=0) -> Tuple[int, str]:
    """
    Function to automatically compile the shared library with the given
    custom logging code and load it with ctypes
    custom logging code and load it with ctypes.

    This function is more or less the main function of this module and unless you know what you're doing with the other functions 
    I recommend using this in function in combination with a function that generates the exact code (like :meth:`~binarycpython.utils.custom_logging_code.binary_c_log_code`)

    returns:
    Args:
        custom_logging_code: string containing the custom logging code
        verbose: Level of verbosity. Defaults to zero if not set explicilty.

    Returns:
        memory adress of the custom logging function in a int type.
    """

@@ -331,6 +401,10 @@ def create_and_load_logging_function(custom_logging_code, verbose=0):
        libcustom_logging.custom_output_function, ctypes.c_void_p
    ).value

    if not isinstance(func_memaddr, int):
        print("Something went wrong. The memory adress returned by the ctypes.cast is not an integer. It has the value {}".format(func_memaddr))
        raise ValueError

    if verbose > 0:
        print(
            "loaded shared library for custom logging. \
+63 −6
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import numpy as np

from binarycpython import _binary_c_bindings


########################################################
# utility functions
########################################################
@@ -36,12 +37,22 @@ def verbose_print(message, verbosity, minimal_verbosity):
        print(message)


def remove_file(file, verbosity=0):
def remove_file(file: str, verbosity: int=0) -> None:
    """
    Function to remove files but with verbosity

    Args:
        file: full filepath to the file that will be removed.

    Returns:
        the path of a subdirectory called binary_c_python in the TMP of the filesystem 

    """

    if os.path.exists(file):
        if not os.path.isfile(file):
            verbose_print("This path ({}) is a directory, not a file".format(file), verbosity, 0)

        try:
            verbose_print("Removed {}".format(file), verbosity, 1)
            os.remove(file)
@@ -50,12 +61,15 @@ def remove_file(file, verbosity=0):
            print("Error while deleting file {}: {}".format(file, inst))


def temp_dir():
def temp_dir()-> str:
    """
    Function to return the path the custom logging library shared object
    and script will be written to.

    Makes use of os.makedirs exist_ok which requires python 3.2+

    Returns:
        the path of a subdirectory called binary_c_python in the TMP of the filesystem 
    """

    tmp_dir = tempfile.gettempdir()
@@ -732,9 +746,10 @@ def get_help_super(print_help=False, fail_silently=True):
    help_all_dict = get_help_all(print_help=False)
    for section_name in help_all_dict:
        section = help_all_dict[section_name]
        print(section_name)
        for parameter_name in section["parameters"].keys():
            print("\t", parameter_name)

        # print(section_name)
        # for parameter_name in section["parameters"].keys():
        #     print("\t", parameter_name)

    help_all_super_dict = help_all_dict.copy()

@@ -781,11 +796,52 @@ def get_help_super(print_help=False, fail_silently=True):
    return help_all_super_dict


def write_binary_c_parameter_descriptions_to_rst_file(output_file):
    """
    Function that calls the binary_c api to get the help text/descriptions for all the paramateres available in that build.
    Writes the results to a .rst file that can be included in the docs. 

    TODO: add the specific version to this document
    """

    # Get the whole arguments dictionary
    arguments_dict = get_help_super()

    if not output_file.endswith(".rst"):
        print("Filename doesn't end with .rst, please provide a proper filename")
        return None

    with open(output_file, 'w') as f:

        print("Binary\\_c parameters", file=f)
        print("{}".format("="*len("Binary\\_c parameters")), file=f)
        print("The following chapter contains all the parameters that the current version of binary\\_c can handle, along with their descriptions and other properties.", file=f)
        print("\n", file=f)

        for el in arguments_dict.keys():
            print("Section: {}".format(el), file=f)
            print("{}\n".format("-"*len("Section: {}".format(el))), file=f)
            # print(arguments_dict[el]['parameters'].keys())

            for arg in arguments_dict[el]['parameters'].keys():
                argdict = arguments_dict[el]['parameters'][arg]

                print("| **Parameter**: {}".format(argdict["param_name"]), file=f)
                print("| **Description**: {}".format(argdict["description"]), file=f)
                if "parameter_value_input_type" in argdict:
                    print("| **Parameter input type**: {}".format(argdict["parameter_value_input_type"]), file=f)
                if "default" in argdict:
                    print("| **Default value**: {}".format(argdict["default"]), file=f)
                if "macros" in argdict:
                    print("| **Macros**: {}".format(argdict['macros']), file=f)
                if not argdict["rest"] == "(null)":
                    print("| **Extra**: {}".format(argdict["rest"]), file=f)
                print("", file=f)

########################################################
# logfile functions
########################################################


def load_logfile(logfile):
    """
    Experimental function that parses the generated logfile of binary_c.
@@ -1014,3 +1070,4 @@ def handle_ensemble_string_to_json(raw_output):

    # return json.loads(json.dumps(ast.literal_eval(raw_output)), cls=binarycDecoder)
    return json.loads(raw_output, cls=binarycDecoder)
+91 −85

File changed.

Preview size limit exceeded, changes collapsed.

+179 −30

File changed.

Preview size limit exceeded, changes collapsed.

+0 −65
Original line number Diff line number Diff line
"""
File containing a dictionary which holds the description for all the grid_options settings. 

Using this, together with the accompanied function, the user can interactively check what the settings result in. 

With this its also possible to automatically generate a pdf file containing all the setting names + descriptions. 

TODO: change the options that one should not use (i.e. the things that are set by the grid itself) to start with an underscore

"""

from binarycpython.utils.grid_options_defaults import grid_options_defaults_dict

# Grid containing the descriptions of the options
grid_options_descriptions = {
    'tmp_dir': "Directory where certain types of output are stored. The grid code is stored in that directory, as well as the custom logging libraries. Log files and other diagnostics will usually be written to this location, unless specified otherwise", # TODO: improve this
    'binary_c_dir': 'Director where binary_c is stored. This options are not really used',
    'binary_c_config_executable': 'Full path of the binary_c-config executable. This option is not really used.',
    'binary_c_executable': 'Full path to the binary_c executable. This options is not really used.',
    'binary_c_shared_library': "Full path to the libbinary_c file. This option is not really used",
    'verbosity': 'Verbosity of the population code. Default is 0, by which only errors will be printed. Higher values will show more output, which is good for debugging.',
    'binary': "Set this to 1 if the population contains binaries. Input: int", # TODO: write what effect this has. 
    'amt_cores': "The amount of cores that the population grid will use. The multiprocessing is useful but make sure to figure out how many logical cores the machine has. The core is multiprocessed, not multithreaded, and will gain no extra speed when amt_cores exceeds the amount of logical cores. Input: int"
}


def grid_options_help(option):
    """
    Function that returns the description of a grid option
    """

    option_keys = grid_options_defaults_dict.keys()
    description_keys = grid_options_descriptions.keys()

    if not option in option_keys:
        print("Error: This is an invalid entry. Option does not exist, please choose from the following options:\n\t{}".format(', '.join(option_keys)))
    else:
        if not option in description_keys:
            print("This option has not been described properly yet. Please contact on of the authors")

        else:
            print(grid_options_descriptions[option])

def grid_options_description_checker(print_info=True):
    """
    Function that checks which descriptions are missing
    """

    option_keys = grid_options_defaults_dict.keys()
    description_keys = grid_options_descriptions.keys()

    undescribed_keys = list(set(option_keys)-set(description_keys))

    if undescribed_keys:
        if print_info:
            print("Warning: the following keys have no description yet:\n\t{}".format(", ".join(sorted(undescribed_keys))))
            print("Total description progress: {:.2f}%%".format(100 * len(description_keys)/len(option_keys)))
    return len(undescribed_keys)


# grid_options_help('amt_cores')

# grid_options_description_checker()