In this blog post, we will be building a simple Pastebin service using Python and Flask. Pastebin is a popular web application used to store plain text or code snippets for a certain period of time. We'll create a basic version that allows users to paste text, select the programming language, and get a URL to share the paste. I have also created a YouTube video about this, which you can view here.
Getting Starting
Before begin creating our application lets setup our environment and in order to setup your environment follow these steps:
- First, Let's create a virtual environment in the project directory.
python -m venv venv
- Now, once we have created the virtual environment, let's activate it and install all the required libraries that are going to be used by this project.
pip install Flask shortuuid pygments
We'll also use shortuuid
for generating unique IDs for each paste and pygments
for syntax highlighting.
- Now that we have installed all the required libraries, let's create the necessary files and folders.
mkdir -p pastes templates static && touch index.py templates/index.html static/styles.css
This is how your folder structure should look:
pastebin/
│
├── app.py
├── pastes/
├── templates/
│ └── index.html
└── static/
└── styles.css
The pastes
directory will store the text files for each paste. The templates directory contains our HTML templates, and the static directory contains CSS for styling.
Now that we have set up the environment, it's time to code.
Writing Code
Let's dive into the code. Create a file named index.py
and add the following code:
from flask import Flask, request, render_template, abort
import shortuuid
import os
from pygments import highlight
from pygments.lexers import get_lexer_by_name, get_all_lexers
from pygments.formatters import HtmlFormatter
app = Flask(__name__)
# Directory to store paste files
PASTE_DIR = 'pastes'
if not os.path.exists(PASTE_DIR):
os.makedirs(PASTE_DIR)
# Function to get available programming languages for syntax highlighting
def get_language_options():
return sorted([(lexer[1][0], lexer[0]) for lexer in get_all_lexers() if lexer[1]])
# Route for the main page
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
# Get content and language from the form
content = request.form['content']
language = request.form['language']
# Generate a unique ID for the paste
paste_id = shortuuid.uuid()
# Create the file path for the paste
file_path = os.path.join(PASTE_DIR, paste_id)
# Save the paste content to a file
with open(file_path, 'w') as f:
f.write(f"{language}\n{content}")
# Generate the URL for the new paste
paste_url = request.url_root + paste_id
return render_template('index.html', paste_url=paste_url, languages=get_language_options())
# Render the form with available languages
return render_template('index.html', languages=get_language_options())
# Route to view a specific paste by its ID
@app.route('/<paste_id>')
def view_paste(paste_id):
# Create the file path for the paste
file_path = os.path.join(PASTE_DIR, paste_id)
if not os.path.exists(file_path):
abort(404) # Return a 404 error if the paste does not exist
# Read the paste file
with open(file_path, 'r') as f:
language = f.readline().strip() # First line is the language
content = f.read() # Remaining content is the paste
# Get the appropriate lexer for syntax highlighting
lexer = get_lexer_by_name(language, stripall=True)
# Create a formatter for HTML output
formatter = HtmlFormatter(linenos=True, cssclass="source")
# Highlight the content
highlighted_content = highlight(content, lexer, formatter)
# Get the CSS for the highlighted content
highlight_css = formatter.get_style_defs('.source')
# Render the paste with syntax highlighting
return render_template('index.html', paste_content=highlighted_content, highlight_css=highlight_css)
if __name__ == '__main__':
app.run(debug=True)
Once you have created the flask now let's create html template in templates/index.html
and style.css
in static/style.css
-
templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pastebin Service</title>
<link rel="stylesheet" href="{{ url_for(\'static\', filename=\'styles.css\') }}">
{% if highlight_css %}
<style>
{{ highlight_css|safe }}
</style>
{% endif %}
</head>
<body>
<h1>Pastebin Service</h1>
{% if paste_url %}
<p>Your paste URL: <a href="{{ paste_url }}">{{ paste_url }}</a></p>
{% endif %}
{% if paste_content %}
<div class="highlight">
{{ paste_content|safe }}
</div>
{% endif %}
<form method="post">
<textarea name="content" rows="10" cols="50" placeholder="Paste your text here..."></textarea><br>
<select name="language">
{% for code, name in languages %}
<option value="{{ code }}">{{ name }}</option>
{% endfor %}
</select><br>
<button type="submit">Submit</button>
</form>
</body>
</html>
-
static/style.css
body {
font-family: Arial, sans-serif;
margin: 20px;
}
h1 {
color: #333;
}
textarea {
width: 100%;
margin-top: 10px;
}
select, button {
margin-top: 10px;
}
.highlight {
background-color: #f5f5f5;
padding: 10px;
border: 1px solid #ccc;
margin-top: 20px;
}
Now that we have created our application, before we run it, let's try to understand how it works by breaking down the code.
Code Breakdown
- First, we import the necessary libraries and modules.
Flask
is our web framework,shortuuid
is used for generating unique IDs, andPygments
is for syntax highlighting. We also set up a directory to store ourpastes/
.
from flask import Flask, request, render_template, abort
import shortuuid
import os
from pygments import highlight
from pygments.lexers import get_lexer_by_name, get_all_lexers
from pygments.formatters import HtmlFormatter
app = Flask(__name__)
PASTE_DIR = 'pastes'
if not os.path.exists(PASTE_DIR):
os.makedirs(PASTE_DIR)
- Then we write a function that retrieves all available programming languages supported by Pygments for syntax highlighting and returns them as a sorted list of tuples.
def get_language_options():
return sorted([(lexer[1][0], lexer[0]) for lexer in get_all_lexers() if lexer[1]])
- Then we write the main route for our application. If the request method is POST (i.e., when the user submits a form), it saves the content and language to a new file with a unique ID. The URL for the new paste is generated and displayed to the user. If the request method is GET, it simply renders the form.
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
content = request.form['content']
language = request.form['language']
paste_id = shortuuid.uuid()
file_path = os.path.join(PASTE_DIR, paste_id)
with open(file_path, 'w') as f:
f.write(f"{language}\n{content}")
paste_url = request.url_root + paste_id
return render_template('index.html', paste_url=paste_url, languages=get_language_options())
return render_template('index.html', languages=get_language_options())
This route handles viewing a specific paste. It reads the paste file, applies syntax highlighting using pygments, and renders the highlighted content.
@app.route('/<paste_id>')
def view_paste(paste_id):
file_path = os.path.join(PASTE_DIR, paste_id)
if not os.path.exists(file_path):
abort(404)
with open(file_path, 'r') as f:
language = f.readline().strip()
content = f.read()
lexer = get_lexer_by_name(language, stripall=True)
formatter = HtmlFormatter(linenos=True, cssclass="source")
highlighted_content = highlight(content, lexer, formatter)
highlight_css = formatter.get_style_defs('.source')
return render_template('index.html', paste_content=highlighted_content, highlight_css=highlight_css)
Now once we understand how everything works, now you can simply run the application using this command
python index.py
Conclusion
You've built a simple Pastebin service using Python and Flask! This service allows users to paste text, select a programming language, and share the paste via a unique URL. You can expand this project by adding features like expiration times for pastes, user authentication, or even a database to store pastes more efficiently.
If you have any feedback, please feel free to leave a comment below. If you prefer not to comment publicly, you can always send me an email.
ORIGINALLY POSTED HERE
Top comments (0)