Snapcrafting a simple flask app

I’ve been trying to package a simple flask “hello world” app but i seem to be running into issues all the time.

Here’s the snapcraft file:

 name: redisapp
 version: 1.0
 summary: redisapp
 description: |
     My redis flask app to get the page hit counter.
 grade: devel
 confinement: devmode
 
 apps:
   redisapp:
     command: python app.py
 
 parts:
   redisapp:
     plugin: python
     python-version: python2
     source: .

here’s the code of the actual app:

 from flask import Flask                                                         
 import os                                                                       
 app = Flask(__name__)                                                           
                                                                                 
 @app.route('/')                                                                 
 def hello():                                                                    
     return 'Hello Nomad !!!'                                                    
                                                                                 
 if __name__ == "__main__":                                                      
     app.run(host="0.0.0.0", debug=True)                                         

I’ve put “flask” as a requirement in requirements.txt and it doesn’t help. Any leads would be appreciated :).

Here’s the output of “snapcraft”

Preparing to pull redisapp 
Pulling redisapp 
Fetching and installing pip...
Traceback (most recent call last):
  File "/usr/lib/python2.7/runpy.py", line 163, in _run_module_as_main
    mod_name, _Error)
  File "/usr/lib/python2.7/runpy.py", line 111, in _get_module_details
    __import__(mod_name)  # Do not catch exceptions initializing package
  File "/usr/lib/python2.7/dist-packages/pip/__init__.py", line 5, in <module>
    import logging
  File "/usr/lib/python2.7/logging/__init__.py", line 26, in <module>
    import sys, os, time, cStringIO, traceback, warnings, weakref, collections
  File "/usr/lib/python2.7/weakref.py", line 14, in <module>
    from _weakref import (
ImportError: cannot import name _remove_dead_weakref
Failed to run '/home/nishant/Documents/Edge/snapcraft/myapp/app/parts/redisapp/install/usr/bin/python2 -m pip download --disable-pip-version-check --dest /home/nishant/Documents/Edge/snapcraft/myapp/app/parts/redisapp/python-packages pip': Exited with code 1.

Have you seen this tutorial?

Maybe you are missing a setup.py

thanks and yeah I have gone through that tutorial. To give you an idea, i just have a simple hello-world kinda script which works in flask-python. I don’t have a setup.py because I don’t think my app needs it? I am just a bit confused about the workflow and why would I need a setup.py for this?

Well, I think you are missing some dependencies when snapcrafting.

You are using the python plugin, see here:

As far as I understand, you need either a setup.py, a requirements.txt, or you need to declare your dependencies (flask) manually in your snapcraft.yaml

I also have a flask project (a bit more complex than yours) and started with a setup.py

hey, thanks for getting back to me and i am still getting stuck. I mean the flask app is just a hello-world script so why would i need a setup.py file? There’s really nothing thats being setup here.

Do you mind sharing your code so that I can atleast get the idea of how you’re packaging it?

Ah, just caught myself directing you in the wrong direction.

It seems to be a python 2.7 error, googling 2 minutes got me there:

https://askubuntu.com/questions/981663/python2-7-broken-by-weakref-import-error-please-help

Back to your question:
I cannot share the complete code, unfortunately. But here the main ideas:

In setup.py I defined the python dependencies:

install_requires=['connexion','flask-cors','smbus2'], # connexion uses flask

Then I defined some entrypoints

entry_points={
        'console_scripts': [
            'app1=app1.__main__:main',
            'app2=app2.__main__:main'
        ],
    },

app1 and app2 are submodules in my directory where also the setup.py locates. I am using these entrypoints as commands in my snapcraft.yaml

apps:
  app1:
    command: bin/app1
    daemon: simple

This might be a little bit of overkill for your application, but maybe you will be missing the flask dependency in your snap later on!

NOTE - I actually deleted my snapcraft.yaml while trying to clean the directory structure so I can make this post look readable. I followed “offlineimap” tutorial FROM HERE to make my yaml file. Its also possible that I screwed up the yaml, can you guide me as to what it would look like considering its just a one single print script.

Hi, so now I am just trying to package a simple “hello world” file which simply prints a joke. This might be a little longer as I am showing everything I’ve got. I would appreciate if you can spend a minute or two.

Here’s the folder structure:

vagrant@vm:~$ tree funniest/
funniest/
├── funniest
│ └── init.py
└── setup.py

1 directory, 2 files

Here’s my setup.py:

vagrant@vm:~$ cat funniest/setup.py
from setuptools import setup

setup(name=‘funniest’,
version=‘0.1’,
description=‘The funniest joke in the world’,
author=‘Flying Circus’,
author_email=‘flyingcircus@example.com’,
packages=[‘funniest’],
zip_safe=False)

Here’s my init.py:

vagrant@vm:~$ cat funniest/funniest/init.py
def joke():
return (u’Wenn ist das Nunst\u00fcck git und Slotermeyer? Ja! … ’
u’Beiherhund das Oder die Flipperwaldt gersput.’)

And here’s the output when I try to build the snap (its the last part of the output which displays the error):

Staging funniest
Priming funniest
Sorry, Snapcraft ran into an error when trying to running through its
lifecycle that generated the following traceback:
Traceback (most recent call last):
File “/snap/snapcraft/1594/bin/snapcraft”, line 11, in
load_entry_point(‘snapcraft==2.42.1’, ‘console_scripts’, ‘snapcraft’)()
File “/snap/snapcraft/1594/lib/python3.5/site-packages/click/core.py”, line 722, in call
return self.main(*args, **kwargs)
File “/snap/snapcraft/1594/lib/python3.5/site-packages/click/core.py”, line 697, in main
rv = self.invoke(ctx)
File “/snap/snapcraft/1594/lib/python3.5/site-packages/click/core.py”, line 1066, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File “/snap/snapcraft/1594/lib/python3.5/site-packages/click/core.py”, line 895, in invoke
return ctx.invoke(self.callback, **ctx.params)
File “/snap/snapcraft/1594/lib/python3.5/site-packages/click/core.py”, line 535, in invoke
return callback(*args, **kwargs)
File “/snap/snapcraft/1594/lib/python3.5/site-packages/snapcraft/cli/lifecycle.py”, line 110, in prime
_execute(‘prime’, parts, **kwargs)
File “/snap/snapcraft/1594/lib/python3.5/site-packages/snapcraft/cli/lifecycle.py”, line 30, in _execute
lifecycle.execute(command, project_options, parts)
File “/snap/snapcraft/1594/lib/python3.5/site-packages/snapcraft/internal/lifecycle/_runner.py”, line 79, in execute
_Executor(config, project_options).run(step, part_names)
File “/snap/snapcraft/1594/lib/python3.5/site-packages/snapcraft/internal/lifecycle/_runner.py”, line 194, in run
self._create_meta(step, part_names)
File “/snap/snapcraft/1594/lib/python3.5/site-packages/snapcraft/internal/lifecycle/_runner.py”, line 245, in _create_meta
self.config.validator.schema)
File “/snap/snapcraft/1594/lib/python3.5/site-packages/snapcraft/internal/meta/_snap_packaging.py”, line 121, in create_snap_packaging
packaging.write_snap_yaml()
File “/snap/snapcraft/1594/lib/python3.5/site-packages/snapcraft/internal/meta/_snap_packaging.py”, line 324, in write_snap_yaml
snap_yaml = self._compose_snap_yaml()
File “/snap/snapcraft/1594/lib/python3.5/site-packages/snapcraft/internal/meta/_snap_packaging.py”, line 480, in _compose_snap_yaml
snap_yaml[‘apps’] = self._wrap_apps(self._config_data[‘apps’])
File “/snap/snapcraft/1594/lib/python3.5/site-packages/snapcraft/internal/meta/_snap_packaging.py”, line 577, in _wrap_apps
self._wrap_app(app, apps[app])
File “/snap/snapcraft/1594/lib/python3.5/site-packages/snapcraft/internal/meta/_snap_packaging.py”, line 585, in _wrap_app
app[k] = self._wrap_exe(app[k], ‘{}-{}’.format(k, name))
File “/snap/snapcraft/1594/lib/python3.5/site-packages/snapcraft/internal/meta/_snap_packaging.py”, line 555, in _wrap_exe
with open(exepath, ‘rb’) as exefile:
FileNotFoundError: [Errno 2] No such file or directory: ‘/home/vagrant/funniest/prime/bin/funniest’

I see, you’re on the right track. But you are missing some fundamental python comprehension.

The tutorials you are using are good, I stumbled also over the “funniest” tutorial a few months ago. You can go more into detail of the offlineimap tutorial when you fetch the source, because the snapcraft here is fetching a pre-existant python source. There must also be a setup.py

The error says there is no bin/funniest. What’s actually your current snapcraft.yaml? There might be a command: bin/funniest which tries to use a programm call, which actually does not exist. Usually, you create this by the entry_points in your setup.py. But I suggest, you should first go without snapcraft. Try to package your python application with setuptools / distutils (see here: https://docs.python.org/2/distutils/introduction.html#distutils-simple-example depends also on your python version). Snapcraft will later on do quite the same.