Sitemap

Memcached CLI using Python

5 min readAug 7, 2024

--

In this article, we will create a memcached cli tool using python.

Memcached Server

Lets Setup a Memcached Server using Docker. First, lets install docker desktop (for Mac): Install Docker Desktop

After starting docker daemon, lets spin up our memcached container.

Step 1: Download memcached image

Press enter or click to view image in full size
Pull memcached image from docker hub
Pull memcached image from docker hub

Step 2: Start memcached container on default port 11211

Press enter or click to view image in full size
Run the docker image on port 11211
Press enter or click to view image in full size
Running memcached container

Now that our memcached server is in action, we can start with our python code for developing cli tool.

Python Code Snippet for CLI tool

Let’s create a main.py file. We will use click library for handling command-line arguments and python-memcached library for interacting with the Memcached server.

Step 1: Import Required Libraries

First, we need to import the necessary libraries. memcache is used to interact with the Memcached server, and click is used to create the CLI.

import memcache
import click

Step 2: Define Default Host and Port

We define default values for the Memcached server’s host and port. These can be overridden by command-line options.

DEFAULT_HOST = 'localhost'
DEFAULT_PORT = 11211

Step 3: Connect to Memcached Server

We create a function connect that takes a host and port as arguments and returns a Memcached client connected to the specified server.

def connect(host, port):
return memcache.Client([f'{host}:{port}'])

Step 4: Initialize the CLI group

We define the main CLI group using click.group(). We also add options for the host and port, which default to the values defined earlier. The cli function initializes the Memcached client and stores it in the click’s context object.

@click.group()
@click.option('-host', default=DEFAULT_HOST, help='Memcached server hostname')
@click.option('-port', default=DEFAULT_PORT, type=int, help='Memcached server port')
@click.pass_context
def cli(ctx, host, port):
'''A simple Memcached CLI client'''
ctx.ensure_object(dict)
ctx.obj['CLIENT'] = connect(host, port)

Step 5: Define CLI Commands

We define several commands for interacting with the Memcached server. Each command uses click.argument to specify required arguments, click.option for optional arguments & click.pass_context to pass click’s context.

Get Ankur Mehta’s stories in your inbox

Join Medium for free to get updates from this writer.

Set Command

@cli.command()
@click.argument('key')
@click.argument('value')
@click.option('-ttl', default=0, help='Expiration time for the data')
@click.pass_context
def set(ctx, key, value, ttl):
'''Set the value of a key'''
client = ctx.obj['CLIENT']
click.echo(f'STORED') if client.set(key, value, time=ttl) else click.echo(f'NOT_STORED')

Add Command

@cli.command()
@click.argument('key')
@click.argument('value')
@click.option('-ttl', default=0, help='Expiration time for the data')
@click.pass_context
def add(ctx, key, value, ttl):
'''Add the value of a non-existing/new key'''
client = ctx.obj['CLIENT']
click.echo(f'STORED') if client.add(key, value, time=ttl) else click.echo(f'NOT_STORED')

Replace Command

@cli.command()
@click.argument('key')
@click.argument('value')
@click.option('-ttl', default=0, help='Expiration time for the data')
@click.pass_context
def replace(ctx, key, value, ttl):
'''Replace existing key with value'''
client = ctx.obj['CLIENT']
click.echo(f'STORED') if client.replace(key, value, time=ttl) else click.echo(f'NOT_STORED')

Append Command

@cli.command()
@click.argument('key')
@click.argument('value')
@click.option('-ttl', default=0, help='Expiration time for the data')
@click.pass_context
def append(ctx, key, value, ttl):
"""Append the value to the end of the existing key's value"""
client = ctx.obj['CLIENT']
click.echo(f'STORED') if client.append(key, value, time=ttl) else click.echo(f'NOT_STORED')

Prepend Command

@cli.command()
@click.argument('key')
@click.argument('value')
@click.option('-ttl', default=0, help='Expiration time for the data')
@click.pass_context
def prepend(ctx, key, value, ttl):
"""Prepend the value to the beginning of the existing key's value"""
client = ctx.obj['CLIENT']
click.echo(f'STORED') if client.prepend(key, value, time=ttl) else click.echo(f'NOT_STORED')

Get Command

@cli.command()
@click.argument('key')
@click.pass_context
def get(ctx, key):
'''Retrieves a key from the memcache'''
client = ctx.obj['CLIENT']
value = client.get(key)
click.echo(f'{value}\nEND')

Delete Command

@cli.command()
@click.argument('key')
@click.pass_context
def delete(ctx, key):
'''Deletes a key from the memcache'''
client = ctx.obj['CLIENT']
click.echo(f'DELETED') if client.delete(key) else click.echo(f'NOT_DELETED')

Increment Command

@cli.command()
@click.argument('key')
@click.argument('delta')
@click.pass_context
def increment(ctx, key, delta):
'''Increment value for existing key by delta'''
client = ctx.obj['CLIENT']
click.echo(f'INCREMENTED') if client.incr(key, delta) else click.echo(f'NOT_INCREMENTED')

Decrement Command

@cli.command()
@click.argument('key')
@click.argument('delta')
@click.pass_context
def decrement(ctx, key, delta):
'''Decrement value for existing key by delta'''
client = ctx.obj['CLIENT']
click.echo(f'DECREMENTED') if client.decr(key, delta) else click.echo(f'NOT_DECREMENTED')

CAS Command

@cli.command()
@click.argument('key')
@click.argument('value')
@click.option('-ttl', default=0, help='Expiration time for the data')
@click.pass_context
def cas(ctx, key, value, ttl):
"""Sets a key to a given value in the memcache if it hasn't been altered since last fetched"""
client = ctx.obj['CLIENT']
click.echo(f'STORED') if client.cas(key, value, time=ttl) else click.echo(f'NOT_STORED')

Setup entry point for our memcached-cli

Lets create a setup.pyfile along with the external dependencies to provide a command-line interface through the ccmm command for easy distribution and installation of our package.

'''setup.py'''

from setuptools import setup


setup(
author='Ankur Mehta',
name='memcached_cli',
description='A simple command line interface for memcached',
version='0.1',
py_modules=['main'],
install_requires=[
'click',
'python-memcached',
],
entry_points='''
[console_scripts]
ccmm=main:cli
''',
)

py_modules lists python modules included in the package.

install_requires lists external packages.

entry_points defines the entry point for our package.

console_scripts indicates we are defining a console script entry point.

ccmm=main:cli means command ccmm should execute cli function from main module

Now, lets install our package using pip after which we can call the ccmmcommand as a standalone command.

pip install .

This will install memcached_cli package along with its dependencies and set up the ccmm command-line tool.

Example


# Set a key with a value and a TTL of 60 seconds
ccmm set my_key my_value -ttl 60

# Add a new key with a value
ccmm add new_key new_value

# Replace an existing key with a new value
ccmm replace existing_key new_value

# Append a value to an existing key
ccmm append existing_key additional_value

# Prepend a value to an existing key
ccmm prepend existing_key additional_value

# Get the value of a key
ccmm get my_key

# Delete a key
ccmm delete my_key

# Increment the value of a key by 5
ccmm increment my_key 5

# Decrement the value of a key by 3
ccmm decrement my_key 3

# Set a key with a value using CAS with a TTL of 60 seconds
ccmm cas my_key my_value -ttl 60

Entire code can be found on Github.

Feel free to give a clap.

--

--