1

I have some code which needs to do very similar things on both Windows and Linux. Unfortunately I require several system-specific functions (e.g. hidden files: Python cross platform hidden file). What is the best way of writing the code for readability and maintainability?

Currently the code uses many if statements to behave differently on the different platforms. Another approach I have considered is to split the code into two separate functions, one for Windows and one for Linux, but this would mean updating the main part of the code in two places.

Note that the main part of the code is considerably longer and more complicated than this.

Combined approach (greatest maintainability but lots of if statements):

import os

def sort_out_files():
    if is_linux:
        do_linux_preparations()
    else:
        do_windows_preparations()

    # Main part of the code:
    for file in os.listdir(folder):
        if is_correct_file(file):
            if is_linux:
                do_main_actions_for_linux()
            else:
                do_main_actions_for_windows()

    if is_linux:
        do_linux_tidying_up()
    else:
        do_windows_tidying_up()

Separate approach (more maintenance needed but fewer if statements required):

import os

def sort_out_files_linux():
    do_linux_preparations()

    # Main part of the code:
    for file in os.listdir(folder):
        if is_correct_file(file):
            do_main_actions_for_linux()

    do_linux_tidying_up()


def sort_out_files_windows():
    do_windows_preparations()

    # Main part of the code:
    for file in os.listdir(folder):
        if is_correct_file(file):
            do_main_actions_for_windows()

    do_windows_tidying_up()

def sort_out_files():
    if is_linux:
        sort_out_files_linux():
    else:
        sort_out_files_windows()

The do_preparations() and do_tidying_up() functions involves copying files, extracting, etc.

is_correct_file() checks that the file has the right name and the right timestamp.

do_main_actions() involves analysing, moving and hiding files.

The examples above both work but don't seem like the most Pythonic or best approach to long-term code maintainability.

SqrtPi
  • 121
  • 2
  • 11
  • Check out this link to get you started: https://www.quora.com/Can-I-run-my-Python-program-in-both-Linux-and-Windows – BeastCoder Nov 02 '19 at 11:46
  • I gave an answer that I think should help you – BeastCoder Nov 02 '19 at 11:54
  • Thanks for the reply! A lot of the code is dependent on the OS such as the ability to make files hidden (https://stackoverflow.com/questions/25432139/python-cross-platform-hidden-file). The code runs fine on both systems but it's long and the many ```if``` statements reduce the readability. – SqrtPi Nov 02 '19 at 11:56

3 Answers3

6

I would put everything that only works for one OS in one file, but with equal name. Then you could add something like this at the beginning of your main file:

if is_linux:
    import linux_tools as tools
else:
    import windows_tools as tools

if both of these files have the same interface (e.g. top level Methods), they can be used interchangeable.

In your example, linux_tools and windows_tools would both contain their respective implementations of sort_out_files, but both named sort_out_files, so you can used it with tools.sort_out_files.

Remember to keep as much common code out of these modules as possible.

MegaIng
  • 7,361
  • 1
  • 22
  • 35
5

In an effort to make your code more maintainable I would recommend looking into the adapter design pattern.

For instance: instead of calling an if statement every time you need to run an os-specific function, you could create an adapter class. On run time you would create the adapter using the appropriate OS-specific implementation and reference it when needed.

An example of an adapter design:

# Adapter interface
class Adapter:
    def SomeAction():
        raise NotImplementedError

    def SomeOtherAction():
        raise NotImplementedError

# Windows implementation
class WindowsAdapter(Adapter):
    def SomeAction():
        print("Windows action!")

    def SomeOtherAction():
        print("Windows other action!")

# Linux implementation
class LinuxAdapter(Adapter):
    def SomeAction():
        print("Linux action!")

    def SomeOtherAction():
        print("Linux other action!")
  • I like this one but it will require more changes to the existing code than the other solutions. I'll seriously consider using it if I need to rebuild. – SqrtPi Nov 02 '19 at 18:11
2

To avoid the duplicate conditions all over your code, you can define all your platform-specific functions in dedicated submodules (one submodules per platform), then conditionally import the one that matches your host

Main file

if is_linux:
    import .fs_platform_windows as fs
else:
    import .fs_platform_linux as fs

def sort_out_files():
    fs.do_preparations()

    # Main part of the code:
    for file in os.listdir(folder):
        if is_correct_file(file):
            fs.do_main_actions()

    fs.do_tidying_up()

Obviously, you need to have both submodules implement the same functions (with the same names). That's some kind of polymorphism, you can get the same result with classes, if you want to have all your code in the same file.

Nielk
  • 760
  • 1
  • 6
  • 22