Skip to main content

Python Refactoring

· 4 min read
koficodes
Just a person

Trying to understand what is refactoring, follow the guide from Real Python. https://realpython.com/python-refactoring

1. Functions That Should Be Objects

Without reading the code, you will not know if it will modify the original image or create a new image.

imagelib.py
def load_image(path):
with open(path, "rb") as file:
fb = file.load()
image = img_lib.parse(fb)
return image


def crop_image(image, width, height):
...
return image


def get_image_thumbnail(image, resolution=100):
...
return image

To call the codes:

from imagelib import load_image, crop_image, get_image_thumbnail

image = load_image('~/face.jpg')
image = crop_image(image, 400, 500)
thumb = get_image_thumbnail(image)

Symptoms of code using functions that could be refactored into classes:

Similar arguments across functions Higher number of Halstead h2 unique operands (All the variables and constants are considered operands) Mix of mutable and immutable functions Functions spread across multiple Python files

Here is a refactored version of those 3 functions, where the following happens:

.init() replaces load_image(). crop() becomes a class method. get_image_thumbnail() becomes a property.

The thumbnail resolution has become a class property, so it can be changed globally or on that particular instance:

imagelib-refactored.py
class Image(object):
thumbnail_resolution = 100

def __init__(self, path):
...

def crop(self, width, height):
...

@property
def thumbnail(self):
...
return thumb

This is how the refactored example would look:

from imagelib import Image

image = Image('~/face.jpg')
image.crop(400, 500)
thumb = image.thumbnail

In the resulting code, we have solved the original problems:

It is clear that thumbnail returns a thumbnail since it is a property, and that it doesn’t modify the instance. The code no longer requires creating new variables for the crop operation.

2. Objects That Should Be Functions

Here are some tell-tale signs of incorrect use of classes:

Classes with 1 method (other than .init()) Classes that contain only static methods

authentication class
class Authenticator(object):
def __init__(self, username, password):
self.username = username
self.password = password


def authenticate(self):
# Do something
return result

It would make more sense to just have a simple function named authenticate() that takes username and password as arguments:

authenticate.py
def authenticate(username, password):
# Do something
return result

3. Converting “Triangular” Code to Flat Code

These are the symptoms of highly nested code:

A high cyclomatic complexity because of the number of code branches A low Maintainability Index because of the high cyclomatic complexity relative to the number of lines of code

def contains_errors(data):
if isinstance(data, list):
for item in data:
if isinstance(item, str):
if item == "error":
return True
return False

Refactor this function by “returning early”

def contains_errors(data):
if not isinstance(data, list):
return False
return data.count("error") > 0

Another technique to reduce nesting by list comprehension

Common practise to create list, loop through and check for criteria.

results = []
for item in iterable:
if item == match:
results.append(item)

Replace with:

results = [item for item in iterable if item == match]

4. Handling Complex Dictionaries With Query Tools

It does have one major side-effect: when dictionaries are highly nested, the code that queries them becomes nested too.

data = {
"network": {
"lines": [
{
"name.en": "Ginza",
"name.jp": "銀座線",
"color": "orange",
"number": 3,
"sign": "G"
},
{
"name.en": "Marunouchi",
"name.jp": "丸ノ内線",
"color": "red",
"number": 4,
"sign": "M"
}
]
}
}

If you wanted to get the line that matched a certain number, this could be achieved in a small function:

def find_line_by_number(data, number):
matches = [line for line in data if line['number'] == number]
if len(matches) > 0:
return matches[0]
else:
raise ValueError(f"Line {number} does not exist.")

find_line_by_number(data["network"]["lines"], 3)

There are third party tools for querying dictionaries in Python. Some of the most popular are JMESPath, glom, asq, and flupy.


import jmespath

jmespath.search("network.lines", data)
[{'name.en': 'Ginza', 'name.jp': '銀座線',
'color': 'orange', 'number': 3, 'sign': 'G'},
{'name.en': 'Marunouchi', 'name.jp': '丸ノ内線',
'color': 'red', 'number': 4, 'sign': 'M'}]

If you wanted to get the line number for every line, you could do this:

jmespath.search("network.lines[*].number", data) [3, 4]

You can provide more complex queries, like a ==or <. The syntax is a little unusual for Python developers, so keep the documentation handy for reference.

Find the line with the number 3

    > > > jmespath.search("network.lines[?number==`3`]", data)
> > > [{'name.en': 'Ginza', 'name.jp': '銀座線', 'color': 'orange', 'number': 3, 'sign': 'G'}]

Using ssh-agent with Visual Studio Code on Windows 10

· 8 min read
koficodes
Just a person

Visual Studio Code is Microsoft’s open-source code editor for Windows, OS X and Linux. Nicely, VS Code has built-in support for Git and support for Python through an extension, making it a useful for scientific development. Using VS Code on Windows is somewhat frustrated, however, if you want to work with a Git repository that was cloned using SSH. Thankfully, I found a workable solution using PuTTY and Git for Windows, such that VS Code transparently works with password-protected SSH keys. Below, I detailed how I got it working in as complete a detail as reasonable, but you may have already done some or even many of these steps. If so, the procedure is actually fairly simple, and consists of pointing Git (and hence VS Code) to use PuTTY and Pageant instead of the SSH version that ships with Git for Windows.

Step 0. Enabling the ssh-agent service

To automatically enable the SSH Agent on Windows, open PowerShell as an Administrator and execute the following commands:

# Ensure that you're running as an Administrator
Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent
Get-Service ssh-agent

Adding SSH Keys

Run the following commands in a terminal window within Visual Studio Code to manage your SSH keys:

Show keys managed by the ssh-agent

ssh-add -l

Adding an SSH Key

ssh-add

For Git integration, set a system environment variable or use a temporary setting in a PowerShell terminal within VSCode.

$env:GIT_SSH="C:\Windows\System32\OpenSSH\ssh.exe"

If you add the line $env:GIT_SSH="C:\Windows\System32\OpenSSH\ssh.exe" to your PowerShell profile, the environment variable will always be used.

Reference

Step 0. Install Required Software

Before we get into things, we’ll need a bit of software. In particular, we’ll need:

tip

Do not install PuTTY from its official homepage, as this will download PuTTY over an insecure connection. This guide will cover how to download PuTTY securely.

For much of this, we can use the Chocolatey package manager for Windows to save some grief, so let’s start by installing that. If you already have Chocolatey, please skip this step. (If you aren’t sure, try running choco from PowerShell.) Run PowerShell as administrator, then run the following command to download and install Chocolatey:

PS> Set-ExecutionPolicy -Scope Process RemoteSigned
PS> iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex

Once this is done, close and reopen PowerShell (again as administrator). This will make choco available as a command. Now we can use it to install Git and OpenSSH (as above, we will not install PuTTY using Chocolatey, as it will download PuTTY from its official homepage using an insecure connection). Run the following PowerShell commands to install Git and OpenSSH:

PS> choco install git
PS> choco install win32-openssh

We’ll finish up by downloading the version of PuTTY that ships with WinSCP, since that version is delivered via HTTPS and not insecure HTTP. In particular, use this link to download PuTTY, then run the installer once you’ve downloaded it.

Step 1. Setup Private Keys

Once everything is installed, we now need to make sure that you have an SSH private key and that this key is registered with your Git hosting service (for instance, GitHub or Bitbucket). If you already have keys and have registered them with your hosting provider, please skip on ahead.

In any case, to generate keys, we’ll again use PowerShell:

ssh-keygen

Simply follow the prompts to make yourself a new public/private key pair, making sure to choose a long (~40 character) passphrase. This passphrase provides much of the entropy for your key, such that it should be much longer than a typical password. Never type your passphrase into a remote password prompt— the passphrase is used to unlock your key locally on your machine, and should never be sent over the network. If a website asks you for your SSH passphrase, you are probably being scammed.

By default, the new keys will be located in C:\Users\<username>\.ssh\id_rsaandC:\Users\<username>\.ssh\id_rsa.pub. As the names suggest, the first of these is theprivatekey and should not be shared with anyone. The other is the public key, and serves to identify yourself to others. Follow the instructions for GitHub or Bitbucket (for Bitbucket, make sure to follow the Linux and OS X instructions, even from Windows) to upload yourpublickey to your hosting provider.

Step 2. Set up SSH Agent

Next, we’ll make sure that your private key is setup in an SSH agent. This will securely remember your passphrase within a given session, so that you don’t have to type it in every time you use it. In particular, we’ll configure Pageant, since this is installed with PuTTY, and works well with a variety of command-line and GUI tools for Windows— most notably, with VS Code.

Pageant must be run at startup in order to be useful, so we’ll begin by adding it to the startup folder now. In Windows Explorer (Windows 8.1 and earlier) or in File Explorer (Windows 10 and later), go to the folder C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup. Right-click inside this folder and select New → Shortcut. From there, browse to C:\Program Files (x86)\PuTTY and select pageant.exe.

Next, we need to import your new key into PuTTY/Pageant.

  1. Run PuTTYgen from the Start Menu and select File → Load Key....
  2. From there, navigate to C:\Users\<username>\.ssh\ and select id_rsa (the private key). You may have to drop down the file types selector in the dialog box to see this, as PuTTYgen defaults to filtering out everything but files ending in *.ppk. Once selected, you’ll be prompted by PuTTY to unlock your key by typing in your passphrase. Do so, and PuTTYgen will show the corresponding public key.
  3. Select File → Save private key to export your private key in PuTTY, rather than OpenSSH, format. I suggest saving it as id_rsa.ppk in the same folder as id_rsa, but this is up to you. Just be sure that to save it in a folder that only you can read, and that is not synchronized using Dropbox, OneDrive, Google Drive or similar.

Finally, run Pageant from the Start Menu (in the future, this will be handled automatically by the shortcut we created above). This will add a new icon to your system tray. It may be hidden by the arrow; if so, click the arrow to make all fo the system tray icons visible. Right-click on Pageant and select Add Key. Browse to where you saved id_rsa.ppk and select it. You’ll be prompted to unlock your key. Upon doing so, your unlocked key will then be made available in Pageant until you log out or quit Pageant.

Step 3. Add SSH Server Fingerprints

Despite the name, this is a short step. Whenever you log into an SSH server, PuTTY will check that the server’s * fingerprint* is correct. This is a short cryptographic string identifying that server, such that checking the fingerprint helps against man-in-the-middle attacks. If you haven’t logged into a server with PuTTY before, however, it has no idea how to check the fingerprint, and will fail to login. Since VS Code ignores these errors, Git support will silently fail unless you first attempt to log into the SSH server offered by your Git host. To do so, we’ll use PowerShell one last time. Run one of the following commands below, depending on which hosting provider you use.

PS > & 'C:\Program Files (x86)\PuTTY\plink.exe' [email protected]
PS > & 'C:\Program Files (x86)\PuTTY\plink.exe' [email protected]

In either case, you’ll be prompted to add the server’s fingerprint to the registry. If you are confident that your traffic is not being intercepted, selectyat this prompt. Neither GitHub nor Bitbucket actually allows logins via SSH, so you’ll get an error, but this is OK: you’ve gotten far enough to see the server’s fingerprint, and that’s all we needed. To check, you can run the commands above again, and note that you are no longer prompted to add the fingerprint, but instead fail immediately.

Step 4. Configure Environment Variables

We’re almost done. All that’s left is to point Git for Windows at PuTTY and Pageant, rather than its own built-in SSH client. Since VS Code uses Git for Windows, this will ensure that VS Code does what we want.

  1. Right-click on My Computer or This PC in Windows/File Explorer, and select Properties.
  2. From there, click Advanced system settings in the sidebar to the left. On the Advanced tab, press the Environment Variables... button at the bottom.
  3. Finally, click New... on the user variables pane (top), and add a new variable named GIT_SSH with value C:\Program Files (x86)\PuTTY\plink.exe.
  4. You may want to use Browse File... in this dialog box to make sure you get the path correct.
  5. Once done, press OK to add the variable,OK again to close the Environment Variables dialog, then OK a third time to close System Properties.
  6. Finally, close the System window.

Ghost Error 500 Hosting on Heroku

· 2 min read
koficodes
Just a person

Ghost Error 500 - The currently active theme is missing.

Bump into this error when I am trying to setup custom theme for Ghost Blog. I am using Heroku to host this site. Just got to know that Heroku site do not have storage to store the custom theme uploaded via Ghost admin panel.

Did couple of search online, a lot suggested that you can clone heroku by git clone command

 heroku git:clone -a myappname

But I get the error message about cloning an empty repository which I think it doesn't make any sense when the site is live at the moment

Continued my search and finally came into something useful which is using heroku slug to download source code

Solution

  1. Before that, proceed to install Heroku CLI following guide at their official site Heroku Dev Center

  2. Follow this site to get it heroku-slugs installed

  3. Login to Heroku account

    heroku login
  4. Heroku slug command to download your source code

    heroku slugs:download -a APP_NAME
  5. You will be getting the following folders:

    Command Line

    - app
    - slug.tar.gz
  6. Make a new repository in GitHub and upload app folder to your repository

  7. Proceed to your heroku app dashboard, under Deploy Tab, Deployment method select GitHub and search for your newly created repository. You will end up something like below.

    Heroku

  8. Done!

  9. You may also setup automatic deploys for every push to master branch

Changing the Most Recent Commit

· 3 min read
koficodes
Just a person

The git commit --amend command allows you to change the most recent commit message.

Not pushed commit

To change the message of the most recent commit that has not been pushed to the remote repository, commit it again using the --amend flag.

  1. Navigate to the repository directory in your terminal.

  2. Run the following command to amend (change) the message of the latest commit:

git commit --amend -m "New commit message."

What the command does is overwriting the most recent commit with the new one.

The -m option allows you to write the new message on the command line without opening an editor session.

Before changing the commit message, you can also add other changes you previously forgot:

git add .

Pushed commit

The amended (changed) commit is a new entity with a different SHA-1. The previous commit will no longer exist in the current branch.

Generally, you should avoid amending a commit that is already pushed as it may cause issues to people who based their work on this commit. It is a good idea to consult your fellow developers before changing a pushed commit.

If you changed the message of the most recently pushed commit, you would have to force push it.

  1. Navigate to the repository.

  2. Amend the message of the latest pushed commit:

git commit --amend -m "New commit message."
  1. Force push to update the history of the remote repository:
git push --force branch-name

Changing an Older or Multiple Commits

If you need to change the message of an older or multiple commits, you can use an interactive git rebase to change one or more older commits.

The rebase command rewrites the commit history, and it is strongly discouraged to rebase commits that are already pushed to the remote Git repository.

  1. Navigate to the repository containing the commit message you want to change.

  2. Type git rebase -i HEAD~N, where N is the number of commits to perform a rebase on. For example, if you want to change the 4th and the 5th latest commits, you would type:

git rebase -i HEAD~5

The command will display the latest X commits in your default text editor:

pick 43f8707f9 fix: update dependency json5 to ^2.1.1
pick cea1fb88a fix: update dependency verdaccio to ^4.3.3
pick aa540c364 fix: update dependency webpack-dev-server to ^3.8.2
pick c5e078656 chore: update dependency flow-bin to ^0.109.0
pick 11ce0ab34 fix: Fix spelling.

# Rebase 7e59e8ead..11ce0ab34 onto 7e59e8ead (5 commands)
  1. Move to the lines of the commit message you want to change and replace pick with reword:
reword 43f8707f9 fix: update dependency json5 to ^2.1.1
reword cea1fb88a fix: update dependency verdaccio to ^4.3.3
pick aa540c364 fix: update dependency webpack-dev-server to ^3.8.2
pick c5e078656 chore: update dependency flow-bin to ^0.109.0
pick 11ce0ab34 fix: Fix spelling.

# Rebase 7e59e8ead..11ce0ab34 onto 7e59e8ead (5 commands)
  1. Save the changes and close the editor.

  2. For each chosen commit, a new text editor window will open. Change the commit message, save the file, and close the editor.

fix: update dependency json5 to ^2.1.1
  1. Force push the changes to the remote repository:
git push --force branch-name

Conclusion

To change the most recent commit message, use the git commit --amend command. To change older or multiple commit messages, use git rebase -i HEAD~N.

Don’t amend pushed commits as it may potentially cause a lot of problems to your colleagues.

Starting With Ghost Blogging

· 2 min read
koficodes
Just a person

Here's my thoughts

Always wanted to create personal site. Started with Github pages, uses automated workflow and get stuff compiled into a static site (Jekyll). Didn't like it much about having to write stuff in editor and then later you have to perform commits and pushes. So I moved on and wanted to find something with a user interface that I could interact with. Something which is free without need to spend extra $$$.

Hugo + Netlify + Github

The next try on Hugo.

Hugo is a static website engine written in Golang. Best thing about this is on the theming system, plenty of choices and everything is free. Did some search on Google, landed on some one click deployment. Messing with it for couple of days on the HTML, CSS and JS. Personal didn't like it due to lack of content management so went for another search on alternatives.

Medium, Wordpress

Recently seeing quite a bit of content creator from Medium and Wordpress, could be possibly everyone is so free during the world pandemic, including myself. These platform don't really work well without spending some $$$. Content creator usually have their articles behind some pay-wall, good for people who wanted to monetize their content. I wish I could write as like the others, but too bad I don't.

Ghost

Finally settled down with hosting Ghost with Heroku with a domain name setup with Cloudflare.

Starting to like it due to the ease of use and user-friendly interface. Best thing about it is open source, you can customise your theme, and you have full control on how your site should look like. The default theme of Ghost (Casper), looks good enough to keep your site look clean and steady.

I am back to Github pages

After the recent announcement on heroku no longer offer free dyno hours. I am moving back to github pages.