4
\$\begingroup\$

What's up smart people. A little backstory for context: I'm a completely self taught dev, and the only person ever looking at my code for the past 6 years is myself. I realize that's not the best idea moving forward, since I now know that this is what I love doing, and what I want to make a career out of. I'd like to fill in the gaps in my knowledge, and become a well rounded developer.

So, I developed this project template over the years, and recently I updated it to be a class by default. The reasoning is that I think classes are easier to work with, since it's easier to reference variables if I just make them part of the class.

I should note that there are some general functions in my toolset I've developed that are called in this template. I think it should be clear what they do.

I also use three modules by default that are not my own, which are simple-settings, logzero, and consolemenu. I have not integrated testing into this yet, because I'm behind in that particular area, but I plan to do so asap.

My questions are:

  1. Is this the right way of thinking for a class?

  2. Should I have one template for a class, one for a non-class project, such as for simple scripts? If so, what are some examples that I might use for each?

  3. I'm big on simplifying for optimization, quick development, easy bug fixing, and readability. What could I simplify or change to improve those things?

  4. Are there are tools, modules, or strategies that I could be using to make development quicker, easier, more readable, etc?

  5. It seems like it's time to make the switch to python 3, but I'm afraid I'll lose too many modules I use in my projects. I'm worried that in any given project, I might end up with a mix of python 2 AND 3 modules that I need to use, such as a module that hasn't ported to 3, or never will (I think most have, but I'm not sure). Is all this true? Should I make the switch, obviously starting with this template?

  6. I tend to grab and save modules I use often for safe keeping, as I'm sortof a content hoarder, a bit afraid something might vanish never to be seen again if I need it in the future. Is this a good practice for very general modules like the settings, logging, and console menu modules I use?

  7. I've recently started being more formal about logging, and good logging practices, and it makes debugging much easy. I know that it's also good practice to code testing in as I go as well, but testing evades me still. I remember that I seem to run into problems importing my chunks of code when using a class. I believe this is because I have a hard time knowing how it would work in my own code, or something about classes that makes it a bit more complex than I know what to do with. I'd like to go as simple and foolproof as possible here, as with everything, so a module that made things easier would be great.

  8. Is my code/style messy and/or bloated?

  9. Just curious, but based on this template, am I at a decent spot considering that I'm self taught, and have only programmed off and on over the last 6 years? Should I be farther along than I am? Or is this not enough to go on to make that judgement?

  10. Any other thoughts, ideas, tips, tricks that would be of obvious help to improve the template and/or my general code based on what you see in this template?

Thanks guys

#! python2.7
# -*- coding: utf-8 -*-

import sys, os, traceback

sys.path.insert(0, 'B:\path-to-tools\PyTools')

from tools import *

## In tools.py: sys.path.insert(0, os.path.realpath(__file__))

""" 

Template v4

This is a class template for python programs. It aims to be a simple yet robust python class to get up and running quickly.

Featuring a very helpful toolset for easy coding, it's also fully set up for logging and settings, as well as building default absolute paths.

"""

class Main(object):

    def __init__(self):

        print_startup_info()

        self.pp = pprint.PrettyPrinter(indent=4)

        ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
        ## ~~~~~~~~~~ Settings ~~~~~~~~~~~ ##
        ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##

        self.settings_list = [
                    'global_settings',
                    'default_paths'
                    ]

        self.sett = load_settings(self.settings_list)

        ## After loading settings, check the directory structure for missing dirs, and fix
        #self.check_directory_structure()

        self.sett = build_abs_paths(self.sett)

        self.finish_abs_paths()

        ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
        ## ~~~~~~~~~~ Logging ~~~~~~~~~~~ ##
        ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##    

        self.handle_init_logging()

        ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
        ## ~~~~~~~~~~ Testing ~~~~~~~~~~~ ##
        ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##    

        if self.sett.MAIN_TESTING:

            self.l.info('''
__file__:           {} 

TEST MODE

LOGGER LEVEL:       {}
OVERRIDE LOG LEVEL: {}'''.format(
            __file__,
            self.sett.MAIN_LOG_LVL, 
            self.sett.LOG_LVL_OVERRIDE))

        ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
        ## ~~~~~~~~~~ Load DB ~~~~~~~~~~~ ##
        ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##

        self.db = self.load_db()

        ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
        ## ~~~~~~~~~ Main Menu ~~~~~~~~~~ ##
        ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##    

        self.main_menu()

    def handle_init_logging(self):

        self.l,self.LOG_LVL = init_logging(
                self.sett.FNAME_LOG_MAIN, 
                self.sett.MAIN_LOG_LVL,
                self.sett.LOG_LVL_OVERRIDE 
                )

        self.l.info(
'''
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
## ~~~~~~~~ Init PROGRAM ~~~~~~~~ ##
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
''')

        self.l.debug(
            'Loaded {} Settings\n{}'.format(
            self.__class__.__name__,
            self.pp.pformat(sorted(
                self.sett.as_dict()))))

        #logzero.loglevel(self.LOG_LVL)

        self.check_log_lvl()        

    def finish_abs_paths(self):

        self.l.debug("Project Abs: {}".format(self.sett.PROJECT_ABS))

        self.sett.APP_ABS      = self.sett.PROJECT_ABS+__file__

        self.sett.MAIN_LOG_ABS = self.sett.LOG_ABS + self.sett.FNAME_LOG_MAIN

        self.sett.FIN_LOG_ABS  = self.sett.LOG_ABS + self.sett.FNAME_LOG_FIN

    def restart(self):

        os.system(os.path.basename(__file__))

    ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
    ## ~~~~~~~~~ Database ~~~~~~~~~~ ##
    ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## 

    def load_db(self):

        self.l.info("Loading database...")

        with open(self.sett.DB_ABS, 'r') as f:

            db = json_easy_load(f.read())

            self.l.debug(
                "Database: \n\n{}".format(db))

            return db

    def check_log_lvl(self):

        self.l.debug('Log Level: {}'.format(self.LOG_LVL))

    ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
    ## ~~~~~~~~ ** Main Menu ** ~~~~~~~~~ ##
    ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##    

    def main_menu(self):

        ## Formatting usage not in docs. You have to look at class in consolemenu.menu_formatting.py
        formatter = MenuFormatBuilder().set_bottom_margin(1).set_top_margin(1).set_right_margin(10).set_header_top_padding(2).set_header_left_padding(4).set_border_style_type(1)

        menu = ConsoleMenu(
                'Title', 
                'Main Menu',
                formatter = formatter,
                clear_screen = False
                )

        f_item = FunctionItem(

                'Menu Option 1', 
                self.function_1, 
                args=[
                    "arg1"
                    ]
                )

        restart = FunctionItem(

                'Restart', 
                self.restart

            )

        menu.append_item(f_item)
        menu.append_item(restart)

        menu.start()
        menu.join()

    def sub_menu(self):

        menu_items = ['Sub Menu Option 1']

        menu = SelectionMenu(menu_items, 'Main Menu')

        ## Submenu
        submenu = SelectionMenu(menu_items, 'Sub Menu')
        submenu_item = SubmenuItem("Show a submenu", submenu, menu=menu)
        menu.append_item(submenu_item)

        menu.show()

        selection = menu.selected_option

        self.l.debug("Selection: {}".format(selection))

        if selection == 0:

            self.l.info("Selected Option 1")

            self.function_2(
                    "arg1",
                    "arg2"
                    )
        else:

            self.l.info("\nNot Option 1\n"


if __name__ == '__main__':

    try:

        main = Main()

    except:

        log_traceback()
\$\endgroup\$
  • 1
    \$\begingroup\$ Welcome to Code Review! Unfortunately there are still modules that haven't been ported yet (and those tend to be too big to port yourself). For a definitive answer on that, you'd need a list and check them all. \$\endgroup\$ – Mast Nov 2 at 7:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.