Press "Enter" to skip to content

Category: PowerShell

A Simple 5x Speed Up With My Django Testing

the statistics in pycharm showing that the built-in method of _hashlib.pbkdf2_hmac is taking 36.4% of the time
PyCharm Profile Stats

More than a third of the time was taken with a hashlib function. My current testing regime doesn’t take long (about 3 seconds on my slow machine) but any iteration time is precious when you are working on your side project.

Before I wax poetic, here’s the changes you’d make to your project’s settings.py

 
# This is only needed if you don't already have a PASSWORD_HASHERS list in your settings 

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]

# DO NOT USE INSECURE HASHING OUTSIDE OF DEBUG MODE OR YOU WILL GET HACKED
# All of your data will be stolen and all of your good works undone 
# Avoid having your company added to this list https://haveibeenpwned.com/
if DEBUG:
    PASSWORD_HASHERS.insert(0, 'django.contrib.auth.hashers.MD5PasswordHasher')

So what’s happening here?

Context

If you’re fairly new to Django you might not know the settings.py file controls the general configuration of your application, and defines arbitrary values available from the settings module (a really useful feature!)

The DEBUG value is set for test environments (earlier in the file) based on environment variables that I control, if you want to learn more check out the DEBUG documentation.

Most values that could exist in your settings file have sane defaults, but it can be a bit confusing that not everything is there at once. If you dont have a PASSWORD_HASHERS list in your settings Django will pick whatever the “right” option is.

In this case we’re defining the standard items and then inserting a new default hash option in the list (during DEBUG mode only.)

This sets PASSWORD_HASHERS to reference a very fast and very weak (full list here).

Using the PyCharm test UI I found my slow machine testing went from 2987ms to 526ms, and improvement of  >5x!

The observer effect is in play for the statistics but we still show the entire hashing process gone from the stats:

PyCharm profiler statistics showing no hashing algorithm in the top items and a much faster result
PyCharm Profile Stats … Much Better

It’s worth repeating – don’t run insecure hashing such as md5 algorithms in production ever. It’s the difference between your password being cracked in seconds and making it impractical for decades or centuries.

It may seem weird, but being purposefully slow is an important feature of cryptographic hashes that you should not attempt to defeat.

If you want to learn more about how cryptographic hash functions work check out Practical Cryptography For Developers.

 

Disable powershell update nag in one line

[System.Environment]::SetEnvironmentVariable("POWERSHELL_UPDATECHECK",0,[System.EnvironmentVariableTarget]::User)

To be clear – I think you should be updating your PowerShell regularly, however the HUGE WHITE BLOCK ACROSS MY ENTIRE SCREEN EVERY TIME I LAUNCH VISUAL STUDIO CODE ISN’T GREAT.

Hated that caps? Yeah, that’s basically my eyes every time I see this nag window inverting the colors across my ennntiiirrreee screen.

I checked the PS repo and some one liners posted didnt work (and used SetEnvironmentVariableTarget which was not a method I had?), so I wanted to make this easy in case you are getting frustrated with the PowerShell update version check message and you want it to go away and didnt want to crack open the environment variables.

Now go update your PowerShell ūüôā

How long did that last PowerShell command take?

Today¬†Thomas Rayner’s post on Get-History reminded me of a one liner I use to calculate time spent on the last command.

(Get-History)[-1].EndExecutionTime - (Get-History)[-1].StartExecutionTime
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 87
Ticks             : 870022
TotalDays         : 1.00696990740741E-06
TotalHours        : 2.41672777777778E-05
TotalMinutes      : 0.00145003666666667
TotalSeconds      : 0.0870022
TotalMilliseconds : 87.0022

 

You can select any property from the output and get just the TotalSeconds, but I like this simple output for when I have to leave some work in progress and I need to come back and check some time in the future.

If you are confused by this code and want further explanations, keep reading!

A typical Get-History call returns the following to Get-Member.

PS C:\temp> Get-History | Get-Member

   TypeName: Microsoft.PowerShell.Commands.HistoryInfo

Name               MemberType Definition
----               ---------- ----------
Clone              Method     Microsoft.PowerShell.Commands.HistoryInfo Clone()
Equals             Method     bool Equals(System.Object obj)
GetHashCode        Method     int GetHashCode()
GetType            Method     type GetType()
ToString           Method     string ToString()
CommandLine        Property   string CommandLine {get;}
EndExecutionTime   Property   datetime EndExecutionTime {get;}
ExecutionStatus    Property   System.Management.Automation.Runspaces.PipelineState ExecutionStatus {get;}
Id                 Property   long Id {get;}
StartExecutionTime Property   datetime StartExecutionTime {get;}

Ignoring the methods (as they are common) we see we have access to some useful properties:

  • The CommandLine¬†which contains the text typed in the console.
  • The ExecutionStatus¬†which tells you if your command was successful.
  • The StartExecutionTime¬†and EndExecutionTime, which store the start time and end time of your running command.
  • The Id, which is just a sequential integer indicating its order in the history.

As is common in PowerShell, even though we see the list of members and properties the information actually only represents one item in the list of objects the command returns, one for each history row.

If you want to see an example this, we can use PowerShell’s comma/unary operator to roll our multiple elements into one bag of elements.

PS C:\temp> $many = Get-History # Assign all our Get-History rows to $many
PS C:\temp> $one = , (Get-History) # Prevent unrolling the items with the , 

PS C:\temp> $many.Count # I ran 18 previous commands when this was assigned
18
PS C:\temp> $one.count # This is a bag holding 18 items
1

This technique was new to me when I first started PowerShell, but a useful trick if you want to write a function that returns an object representing the entire list, instead of returning a series of elements. If you want to learn more about the Comma operator, check the documentation in about_Operators.

The next step is to ask for the first element of a list, which PowerShell supports with bracket notation (list)[element_number], with 0 being the first number (as all sane programming languages choose.)

We use dot notation to access the properties of EndExecutionTime and StartExecutionTime, once each for our specific list item, (list)[element_number].property.

Lastly, we subtract the properties from one another (list)[element_number].property Р(list)[element_number].property, which PowerShell determines behind the scenes is two dates being subtracted, performs the required math, and usefully returns a useful System.TimeSpan type representing the time between the two dates.

Special thanks to Contributing Editor John G Hohengarten for his thoughtful additions to this post.