John David

John David

1563174885

Python, Ruby, and Golang: A Command-Line Application Comparison

In late 2014 I built a tool called pymr. I recently felt the need to learn golang and refresh my ruby knowledge so I decided to revisit the idea of pymr and build it in multiple languages. In this post I will break down the “mr” (merr) application (pymr, gomr, rumr) and present the implementation of specific pieces in each language. I will provide an overall personal preference at the end but will leave the comparison of individual pieces up to you.

Application Structure

The basic idea of this application is that you have some set of related directories that you want to execute a single command on. The “mr” tool provides a method for registering directories, and a method for running commands on groups of registered directories. The application has the following components:

  • A command-line interface
  • A registration command (writes a file with given tags)
  • A run command (runs a given command on registered directories)

Command-Line Interface

The command line interface for the “mr” tools is:

$ pymr --help
Usage: pymr [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  register  register a directory
  run       run a given command in matching...

To compare building the command-line interface let’s take a look at the register command in each language.

Python (pymr)

To build the command line interface in python I chose to use the click package.

@pymr.command()
@click.option('--directory', '-d', default='./')
@click.option('--tag', '-t', multiple=True)
@click.option('--append', is_flag=True)
def register(directory, tag, append):
    ...

Ruby (rumr)

To build the command line interface in ruby I chose to use the thor gem.

desc 'register', 'Register a directory'
method_option :directory,
              aliases: '-d',
              type: :string,
              default: './',
              desc: 'Directory to register'
method_option :tag,
              aliases: '-t',
              type: :array,
              default: 'default',
              desc: 'Tag/s to register'
method_option :append,
              type: :boolean,
              desc: 'Append given tags to any existing tags?'
def register
  ...

Golang (gomr)

To build the command line interface in Golang I chose to use the cli.go package.

app.Commands = []cli.Command{
    {
        Name:   "register",
        Usage:  "register a directory",
        Action: register,
        Flags: []cli.Flag{
            cli.StringFlag{
                Name:  "directory, d",
                Value: "./",
                Usage: "directory to tag",
            },
            cli.StringFlag{
                Name:  "tag, t",
                Value: "default",
                Usage: "tag to add for directory",
            },
            cli.BoolFlag{
                Name:  "append",
                Usage: "append the tag to an existing registered directory",
            },
        },
    },
}

Registration

The registration logic is as follows:

  1. If the user asks to --append read the .[py|ru|go]mr file if it exists.
  2. Merge the existing tags with the given tags.
  3. Write a new .[...]mr file with the new tags.

This breaks down into a few small tasks we can compare in each language:

  • Searching for and reading a file.
  • Merging two items (only keeping the unique set)
  • Writing a file

File Search

Python (pymr)

For python this involves the os module.

pymr_file = os.path.join(directory, '.pymr')
if os.path.exists(pymr_file):
    # ...

Ruby (rumr)

For ruby this involves the File class.

rumr_file = File.join(directory, '.rumr')
if File.exist?(rumr_file)
    # ...

Golang (gomr)

For golang this involves the path package.

fn := path.Join(directory, ".gomr")
if _, err := os.Stat(fn); err == nil {
    // ...
}

Unique Merge

Python (pymr)

For python this involves the use of a set.

# new_tags and cur_tags are tuples
new_tags = tuple(set(new_tags + cur_tags))

Ruby (rumr)

For ruby this involves the use of the .uniq array method.

# Edited (5/31)
# old method:
#  new_tags = (new_tags + cur_tags).uniq

# new_tags and cur_tags are arrays
new_tags |= cur_tags

Golang (gomr)

For golang this involves the use of custom function.

func AppendIfMissing(slice []string, i string) []string {
    for _, ele := range slice {
        if ele == i {
            return slice
        }
    }
    return append(slice, i)
}

for _, tag := range strings.Split(curTags, ",") {
    newTags = AppendIfMissing(newTags, tag)
}

File Read/Write

I tried to choose the simplest possible file format to use in each language.

Python (pymr)

For python this involves the use of the pickle module.

# read
cur_tags = pickle.load(open(pymr_file))

# write
pickle.dump(new_tags, open(pymr_file, 'wb'))

Ruby (rumr)

For ruby this involves the use of the YAML module.

# read
cur_tags = YAML.load_file(rumr_file)

# write
# Edited (5/31)
# old method:
#  File.open(rumr_file, 'w') { |f| f.write new_tags.to_yaml }
IO.write(rumr_file, new_tags.to_yaml)

Golang (gomr)

For golang this involves the use of the config package.

// read
cfg, _ := config.ReadDefault(".gomr")

// write
outCfg.WriteFile(fn, 0644, "gomr configuration file")

Run (Command Execution)

The run logic is as follows:

  1. Recursively walk from the given basepath searching for .[...]mr files
  2. Load a found file, and see if the given tag is in it
  3. Call the given command in the directory of a matching file.

This breaks down into a few small tasks we can compare in each language:

  • Recursive Directory Search
  • String Comparison
  • Calling a Shell Command

Recursive Directory Search

Python (pymr)

For python this involves the os module and fnmatch module.

for root, _, fns in os.walk(basepath):
    for fn in fnmatch.filter(fns, '.pymr'):
        # ...

Ruby (rumr)

For ruby this involves the Find and File classes.

# Edited (5/31)
# old method:
#  Find.find(basepath) do |path|
#        next unless File.basename(path) == '.rumr'
Dir[File.join(options[:basepath], '**/.rumr')].each do |path|
    # ...

Golang (gomr)

For golang this requires the filepath package and a custom callback function.

func RunGomr(ctx *cli.Context) filepath.WalkFunc {
    return func(path string, f os.FileInfo, err error) error {
        // ...
        if strings.Contains(path, ".gomr") {
            // ...
        }
    }
}

filepath.Walk(root, RunGomr(ctx))

String Comparison

Python (pymr)

Nothing additional is needed in python for this task.

if tag in cur_tags:
    # ...

Ruby (rumr)

Nothing additional is needed in ruby for this task.

if cur_tags.include? tag
    # ...

Golang (gomr)

For golang this requires the strings package.

if strings.Contains(cur_tags, tag) {
    // ...
}

Calling a Shell Command

Python (pymr)

For python this requires the os module and the subprocess module.

os.chdir(root)
subprocess.call(command, shell=True)

Ruby (rumr)

For ruby this involves the Kernel module and the Backticks syntax.

# Edited (5/31)
# old method
#  puts `bash -c "cd #{base_path} && #{command}"`
Dir.chdir(File.dirname(path)) { puts `#{command}` }

Golang (gomr)

For golang this involves the os package and the os/exec package.

os.Chdir(filepath.Dir(path))
cmd := exec.Command("bash", "-c", command)
stdout, err := cmd.Output()

Packaging

The ideal mode of distribution for this tool is via a package. A user could then install it tool install [pymr,rumr,gomr] and have a new command on there systems path to execute. I don’t want to go into packaging systems here, rather I will just show the basic configuration file needed in each language.

Python (pymr)

For python a setup.py is required. Once the package is created and uploaded it can be installed with pip install pymr.

from setuptools import setup, find_packages

classifiers = [
    'Environment :: Console',
    'Operating System :: OS Independent',
    'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
    'Intended Audience :: Developers',
    'Programming Language :: Python',
    'Programming Language :: Python :: 2',
    'Programming Language :: Python :: 2.7'
]

setuptools_kwargs = {
    'install_requires': [
        'click>=4,<5'
    ],
    'entry_points': {
        'console_scripts': [
            'pymr = pymr.pymr:pymr',
        ]
    }
}

setup(
    name='pymr',
    description='A tool for executing ANY command in a set of tagged directories.',
    author='Kyle W Purdon',
    author_email='kylepurdon@gmail.com',
    url='https://github.com/kpurdon/pymr',
    download_url='https://github.com/kpurdon/pymr',
    version='2.0.1',
    packages=find_packages(),
    classifiers=classifiers,
    **setuptools_kwargs
)

Ruby (rumr)

For ruby a rumr.gemspec is required. Once the gem is created and uploaded is can be installed with gem install rumr.

Gem::Specification.new do |s|
  s.name        = 'rumr'
  s.version     = '1.0.0'
  s.summary     = 'Run system commands in sets' \
                  ' of registered and tagged directories.'
  s.description = '[Ru]by [m]ulti-[r]epository Tool'
  s.authors     = ['Kyle W. Purdon']
  s.email       = 'kylepurdon@gmail.com'
  s.files       = ['lib/rumr.rb']
  s.homepage    = 'https://github.com/kpurdon/rumr'
  s.license     = 'GPLv3'
  s.executables << 'rumr'
  s.add_dependency('thor', ['~>0.19.1'])
end

Golang (gomr)

For golang the source is simply compiled into a binary that can be redistributed. There is no additional file needed and currently no package repository to push to.

Conclusion

For this tool Golang feels like the wrong choice. I don’t need it to be very performant and I’m not utilizing the native concurrency Golang has to offer. This leaves me with Ruby and Python. For about 80% of the logic my personal preference is a toss-up between the two. Here are the pieces I find better in one language:

Command-Line Interface Declaration

Python is the winner here. The click libraries decorator style declaration is clean and simple. Keep in mind I have only tried the Ruby thor gem so there may be better solutions in Ruby. This is also not a commentary on either language, rather that the CLI library I used in python is my preference.

Recursive Directory Search

Ruby is the winner here. I found that this entire section of code was much cleaner and more readable using ruby’s Find.find() and especially the next unless syntax.

Packaging

Ruby is the winner here. The rumr.gemspec file is much simpler and the process of building and pushing a gem was much simpler as well. The bundler tool also makes installing in semi-isolated environments a snap.

Final Determination

Because of packaging and the recursive directory search preference I would choose Ruby as the tool for this application. However the differences in preference were so minor that Python would be more than fitting as well. Golang however, is not the correct tool here.

Thanks for reading ❤

#python #ruby #go

What is GEEK

Buddha Community

Python, Ruby, and Golang: A Command-Line Application Comparison
Ray  Patel

Ray Patel

1619518440

top 30 Python Tips and Tricks for Beginners

Welcome to my Blog , In this article, you are going to learn the top 10 python tips and tricks.

1) swap two numbers.

2) Reversing a string in Python.

3) Create a single string from all the elements in list.

4) Chaining Of Comparison Operators.

5) Print The File Path Of Imported Modules.

6) Return Multiple Values From Functions.

7) Find The Most Frequent Value In A List.

8) Check The Memory Usage Of An Object.

#python #python hacks tricks #python learning tips #python programming tricks #python tips #python tips and tricks #python tips and tricks advanced #python tips and tricks for beginners #python tips tricks and techniques #python tutorial #tips and tricks in python #tips to learn python #top 30 python tips and tricks for beginners

Ray  Patel

Ray Patel

1619510796

Lambda, Map, Filter functions in python

Welcome to my Blog, In this article, we will learn python lambda function, Map function, and filter function.

Lambda function in python: Lambda is a one line anonymous function and lambda takes any number of arguments but can only have one expression and python lambda syntax is

Syntax: x = lambda arguments : expression

Now i will show you some python lambda function examples:

#python #anonymous function python #filter function in python #lambda #lambda python 3 #map python #python filter #python filter lambda #python lambda #python lambda examples #python map

Art  Lind

Art Lind

1602968400

Python Tricks Every Developer Should Know

Python is awesome, it’s one of the easiest languages with simple and intuitive syntax but wait, have you ever thought that there might ways to write your python code simpler?

In this tutorial, you’re going to learn a variety of Python tricks that you can use to write your Python code in a more readable and efficient way like a pro.

Let’s get started

Swapping value in Python

Instead of creating a temporary variable to hold the value of the one while swapping, you can do this instead

>>> FirstName = "kalebu"
>>> LastName = "Jordan"
>>> FirstName, LastName = LastName, FirstName 
>>> print(FirstName, LastName)
('Jordan', 'kalebu')

#python #python-programming #python3 #python-tutorials #learn-python #python-tips #python-skills #python-development

Art  Lind

Art Lind

1602666000

How to Remove all Duplicate Files on your Drive via Python

Today you’re going to learn how to use Python programming in a way that can ultimately save a lot of space on your drive by removing all the duplicates.

Intro

In many situations you may find yourself having duplicates files on your disk and but when it comes to tracking and checking them manually it can tedious.

Heres a solution

Instead of tracking throughout your disk to see if there is a duplicate, you can automate the process using coding, by writing a program to recursively track through the disk and remove all the found duplicates and that’s what this article is about.

But How do we do it?

If we were to read the whole file and then compare it to the rest of the files recursively through the given directory it will take a very long time, then how do we do it?

The answer is hashing, with hashing can generate a given string of letters and numbers which act as the identity of a given file and if we find any other file with the same identity we gonna delete it.

There’s a variety of hashing algorithms out there such as

  • md5
  • sha1
  • sha224, sha256, sha384 and sha512

#python-programming #python-tutorials #learn-python #python-project #python3 #python #python-skills #python-tips

John David

John David

1563174885

Python, Ruby, and Golang: A Command-Line Application Comparison

In late 2014 I built a tool called pymr. I recently felt the need to learn golang and refresh my ruby knowledge so I decided to revisit the idea of pymr and build it in multiple languages. In this post I will break down the “mr” (merr) application (pymr, gomr, rumr) and present the implementation of specific pieces in each language. I will provide an overall personal preference at the end but will leave the comparison of individual pieces up to you.

Application Structure

The basic idea of this application is that you have some set of related directories that you want to execute a single command on. The “mr” tool provides a method for registering directories, and a method for running commands on groups of registered directories. The application has the following components:

  • A command-line interface
  • A registration command (writes a file with given tags)
  • A run command (runs a given command on registered directories)

Command-Line Interface

The command line interface for the “mr” tools is:

$ pymr --help
Usage: pymr [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  register  register a directory
  run       run a given command in matching...

To compare building the command-line interface let’s take a look at the register command in each language.

Python (pymr)

To build the command line interface in python I chose to use the click package.

@pymr.command()
@click.option('--directory', '-d', default='./')
@click.option('--tag', '-t', multiple=True)
@click.option('--append', is_flag=True)
def register(directory, tag, append):
    ...

Ruby (rumr)

To build the command line interface in ruby I chose to use the thor gem.

desc 'register', 'Register a directory'
method_option :directory,
              aliases: '-d',
              type: :string,
              default: './',
              desc: 'Directory to register'
method_option :tag,
              aliases: '-t',
              type: :array,
              default: 'default',
              desc: 'Tag/s to register'
method_option :append,
              type: :boolean,
              desc: 'Append given tags to any existing tags?'
def register
  ...

Golang (gomr)

To build the command line interface in Golang I chose to use the cli.go package.

app.Commands = []cli.Command{
    {
        Name:   "register",
        Usage:  "register a directory",
        Action: register,
        Flags: []cli.Flag{
            cli.StringFlag{
                Name:  "directory, d",
                Value: "./",
                Usage: "directory to tag",
            },
            cli.StringFlag{
                Name:  "tag, t",
                Value: "default",
                Usage: "tag to add for directory",
            },
            cli.BoolFlag{
                Name:  "append",
                Usage: "append the tag to an existing registered directory",
            },
        },
    },
}

Registration

The registration logic is as follows:

  1. If the user asks to --append read the .[py|ru|go]mr file if it exists.
  2. Merge the existing tags with the given tags.
  3. Write a new .[...]mr file with the new tags.

This breaks down into a few small tasks we can compare in each language:

  • Searching for and reading a file.
  • Merging two items (only keeping the unique set)
  • Writing a file

File Search

Python (pymr)

For python this involves the os module.

pymr_file = os.path.join(directory, '.pymr')
if os.path.exists(pymr_file):
    # ...

Ruby (rumr)

For ruby this involves the File class.

rumr_file = File.join(directory, '.rumr')
if File.exist?(rumr_file)
    # ...

Golang (gomr)

For golang this involves the path package.

fn := path.Join(directory, ".gomr")
if _, err := os.Stat(fn); err == nil {
    // ...
}

Unique Merge

Python (pymr)

For python this involves the use of a set.

# new_tags and cur_tags are tuples
new_tags = tuple(set(new_tags + cur_tags))

Ruby (rumr)

For ruby this involves the use of the .uniq array method.

# Edited (5/31)
# old method:
#  new_tags = (new_tags + cur_tags).uniq

# new_tags and cur_tags are arrays
new_tags |= cur_tags

Golang (gomr)

For golang this involves the use of custom function.

func AppendIfMissing(slice []string, i string) []string {
    for _, ele := range slice {
        if ele == i {
            return slice
        }
    }
    return append(slice, i)
}

for _, tag := range strings.Split(curTags, ",") {
    newTags = AppendIfMissing(newTags, tag)
}

File Read/Write

I tried to choose the simplest possible file format to use in each language.

Python (pymr)

For python this involves the use of the pickle module.

# read
cur_tags = pickle.load(open(pymr_file))

# write
pickle.dump(new_tags, open(pymr_file, 'wb'))

Ruby (rumr)

For ruby this involves the use of the YAML module.

# read
cur_tags = YAML.load_file(rumr_file)

# write
# Edited (5/31)
# old method:
#  File.open(rumr_file, 'w') { |f| f.write new_tags.to_yaml }
IO.write(rumr_file, new_tags.to_yaml)

Golang (gomr)

For golang this involves the use of the config package.

// read
cfg, _ := config.ReadDefault(".gomr")

// write
outCfg.WriteFile(fn, 0644, "gomr configuration file")

Run (Command Execution)

The run logic is as follows:

  1. Recursively walk from the given basepath searching for .[...]mr files
  2. Load a found file, and see if the given tag is in it
  3. Call the given command in the directory of a matching file.

This breaks down into a few small tasks we can compare in each language:

  • Recursive Directory Search
  • String Comparison
  • Calling a Shell Command

Recursive Directory Search

Python (pymr)

For python this involves the os module and fnmatch module.

for root, _, fns in os.walk(basepath):
    for fn in fnmatch.filter(fns, '.pymr'):
        # ...

Ruby (rumr)

For ruby this involves the Find and File classes.

# Edited (5/31)
# old method:
#  Find.find(basepath) do |path|
#        next unless File.basename(path) == '.rumr'
Dir[File.join(options[:basepath], '**/.rumr')].each do |path|
    # ...

Golang (gomr)

For golang this requires the filepath package and a custom callback function.

func RunGomr(ctx *cli.Context) filepath.WalkFunc {
    return func(path string, f os.FileInfo, err error) error {
        // ...
        if strings.Contains(path, ".gomr") {
            // ...
        }
    }
}

filepath.Walk(root, RunGomr(ctx))

String Comparison

Python (pymr)

Nothing additional is needed in python for this task.

if tag in cur_tags:
    # ...

Ruby (rumr)

Nothing additional is needed in ruby for this task.

if cur_tags.include? tag
    # ...

Golang (gomr)

For golang this requires the strings package.

if strings.Contains(cur_tags, tag) {
    // ...
}

Calling a Shell Command

Python (pymr)

For python this requires the os module and the subprocess module.

os.chdir(root)
subprocess.call(command, shell=True)

Ruby (rumr)

For ruby this involves the Kernel module and the Backticks syntax.

# Edited (5/31)
# old method
#  puts `bash -c "cd #{base_path} && #{command}"`
Dir.chdir(File.dirname(path)) { puts `#{command}` }

Golang (gomr)

For golang this involves the os package and the os/exec package.

os.Chdir(filepath.Dir(path))
cmd := exec.Command("bash", "-c", command)
stdout, err := cmd.Output()

Packaging

The ideal mode of distribution for this tool is via a package. A user could then install it tool install [pymr,rumr,gomr] and have a new command on there systems path to execute. I don’t want to go into packaging systems here, rather I will just show the basic configuration file needed in each language.

Python (pymr)

For python a setup.py is required. Once the package is created and uploaded it can be installed with pip install pymr.

from setuptools import setup, find_packages

classifiers = [
    'Environment :: Console',
    'Operating System :: OS Independent',
    'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
    'Intended Audience :: Developers',
    'Programming Language :: Python',
    'Programming Language :: Python :: 2',
    'Programming Language :: Python :: 2.7'
]

setuptools_kwargs = {
    'install_requires': [
        'click>=4,<5'
    ],
    'entry_points': {
        'console_scripts': [
            'pymr = pymr.pymr:pymr',
        ]
    }
}

setup(
    name='pymr',
    description='A tool for executing ANY command in a set of tagged directories.',
    author='Kyle W Purdon',
    author_email='kylepurdon@gmail.com',
    url='https://github.com/kpurdon/pymr',
    download_url='https://github.com/kpurdon/pymr',
    version='2.0.1',
    packages=find_packages(),
    classifiers=classifiers,
    **setuptools_kwargs
)

Ruby (rumr)

For ruby a rumr.gemspec is required. Once the gem is created and uploaded is can be installed with gem install rumr.

Gem::Specification.new do |s|
  s.name        = 'rumr'
  s.version     = '1.0.0'
  s.summary     = 'Run system commands in sets' \
                  ' of registered and tagged directories.'
  s.description = '[Ru]by [m]ulti-[r]epository Tool'
  s.authors     = ['Kyle W. Purdon']
  s.email       = 'kylepurdon@gmail.com'
  s.files       = ['lib/rumr.rb']
  s.homepage    = 'https://github.com/kpurdon/rumr'
  s.license     = 'GPLv3'
  s.executables << 'rumr'
  s.add_dependency('thor', ['~>0.19.1'])
end

Golang (gomr)

For golang the source is simply compiled into a binary that can be redistributed. There is no additional file needed and currently no package repository to push to.

Conclusion

For this tool Golang feels like the wrong choice. I don’t need it to be very performant and I’m not utilizing the native concurrency Golang has to offer. This leaves me with Ruby and Python. For about 80% of the logic my personal preference is a toss-up between the two. Here are the pieces I find better in one language:

Command-Line Interface Declaration

Python is the winner here. The click libraries decorator style declaration is clean and simple. Keep in mind I have only tried the Ruby thor gem so there may be better solutions in Ruby. This is also not a commentary on either language, rather that the CLI library I used in python is my preference.

Recursive Directory Search

Ruby is the winner here. I found that this entire section of code was much cleaner and more readable using ruby’s Find.find() and especially the next unless syntax.

Packaging

Ruby is the winner here. The rumr.gemspec file is much simpler and the process of building and pushing a gem was much simpler as well. The bundler tool also makes installing in semi-isolated environments a snap.

Final Determination

Because of packaging and the recursive directory search preference I would choose Ruby as the tool for this application. However the differences in preference were so minor that Python would be more than fitting as well. Golang however, is not the correct tool here.

Thanks for reading ❤

#python #ruby #go