<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2013, Bruno Gonzalez <stenyak@stenyak.com>. This software is licensed under the Affero General Public License version 3 or later.  See the LICENSE file.

import os, urllib, time
import json
from functools import wraps

from flask import request, g, session, flash, url_for, abort, Flask, redirect, send_file, render_template
from flask_openid import OpenID
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

from gallery import Gallery
from config import get_config as cfg

def init_globals(name, db_uri):
    def init_flask_openid(app):
        return OpenID(app)
    def init_flask(name, db_uri):
        app = Flask(name)
        app.config.update(DATABASE_URI=db_uri, SECRET_KEY='development key', DEBUG=True)
        return app
    def init_sqlalchemy(db_uri):
        engine = create_engine(db_uri)
        db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
        base = declarative_base()
        base.query = db_session.query_property()
        return engine, base, db_session
    app = init_flask(name, db_uri)
    oid = init_flask_openid(app)
    engine, base, db_session = init_sqlalchemy(db_uri)
    return app, oid, engine, base, db_session

db_uri = "sqlite:///" + "profile.db"
app, oid, engine, Base, db_session = init_globals(__name__, db_uri)

def init_db():
    global engine
    global Base
    Base.metadata.create_all(bind=engine)

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(60))
    email = Column(String(200))
    openid = Column(String(200))
    groups_string = Column(String(200))

    def __init__(self, name, email, openid, groups_string):
        self.name = name
        self.email = email
        self.openid = openid
        self.groups_string = groups_string
    def get_groups(self):
        return self.groups_string.split()

def db_created():
    try:
        User.query.filter_by(openid="foobar").first()
        return True
    except:
        return False

def db_required(fn):
    @wraps(fn)
    def inner(*args, **kwargs):
        if not db_created():
            init_db()
        return fn(*args, **kwargs)
    return inner

def render_time(fn):
    @wraps(fn)
    def inner(*args, **kwargs):
        start = time.time()
        result = fn(*args, **kwargs)
        end = time.time()
        try:
            return result.replace("TTTTIME", "%.4f" %(end-start))
        except AttributeError:
            return result
    return inner

@app.before_request
@db_required
def before_request():
    g.user = None
    if 'openid' in session:
        g.user = User.query.filter_by(openid=session['openid']).first()

@app.after_request
def after_request(response):
    db_session.remove()
    return response

@app.route('/favicon.ico')
def favicon():
    return send_file("static/favicon.ico")

@app.route('/')
def index():
    return redirect("/gallery/data")

def get_openid_providers():
    class OpenIdProvider():
        def __init__(self, name, url, hidden=True):
            self.name = name
            self.url = url
            self.image = "openid/" + name.replace(" ", "").lower() + ".png"
            self.hidden = hidden
    return [
    OpenIdProvider("Google", "https://www.google.com/accounts/o8/id"),
    OpenIdProvider("Yahoo", "yahoo.com"),
    OpenIdProvider("WordPress", "Your WordPress blog url", False),
    OpenIdProvider("Flickr", "flickr.com"),
    OpenIdProvider("Stack Exchange", "https://openid.stackexchange.com"),
    OpenIdProvider("Blogger", "Your Blogger blog url", False),
    OpenIdProvider("LiveJournal", "Your LiveJournal blog url", False),
    OpenIdProvider("OpenID", "Your custom OpenID url", False)
    ]

def is_in_group(user, group):
    return user is not None and group in user.get_groups()

def render(*args, **kwargs):
    global g
    user = g.user
    return render_template(*args, debug=cfg()["debug_mode_enabled"], user=user, **kwargs)

@app.route('/login', methods=['GET', 'POST'])
@render_time
@oid.loginhandler
def login():
    """Does the login via OpenID.  Has to call into `oid.try_login` to start the OpenID machinery.  """
    # if we are already logged in, go back to were we came from
    if g.user is not None:
        return redirect(oid.get_next_url())
    if request.method == 'POST':
        openid = request.form.get('openid')
        if openid:
            return oid.try_login(openid, ask_for=['email', 'fullname', 'nickname'])
    return render('login.html', next=oid.get_next_url(), error=oid.fetch_error(), providers=get_openid_providers())

@db_required
@oid.after_login
def create_or_login(resp):
    """This is called when login with OpenID succeeded and it's not necessary to figure out if this is the users's first login or not.  This function has to redirect otherwise the user will be presented with a terrible URL which we certainly don't want.  """
    session['openid'] = resp.identity_url
    user = User.query.filter_by(openid=resp.identity_url).first()
    if user is not None:
        flash(u'Successfully signed in')
        g.user = user
        return redirect(oid.get_next_url())
    return redirect(url_for('create_profile', next=oid.get_next_url(), name=resp.fullname or resp.nickname, email=resp.email))

@app.route('/create-profile', methods=['GET', 'POST'])
@render_time
def create_profile():
    """If this is the user's first login, the create_or_login function will redirect here so that the user can set up his profile.  """
    if g.user is not None or 'openid' not in session:
        return redirect(url_for('index'))
    if not cfg()["profile_creation_enabled"]:
        return render('create_profile.html', authn_error="profile creation has been disabled by the administrator.")
    if request.method == 'POST':
        name = request.form['name']
        email = request.form['email']
        if not name:
            flash(u'Error: you have to provide a name')
        elif '@' not in email:
            flash(u'Error: you have to enter a valid email address')
        else:
            flash(u'Profile successfully created')
            groups_string = ""
            if len(User.query.all()) == 0:
                groups_string = "administrators"
            db_session.add(User(name, email, session['openid'], groups_string))
            db_session.commit()
            return redirect(oid.get_next_url())
    return render('create_profile.html', next_url=oid.get_next_url())

@app.route('/profiles', methods=['GET', 'POST'])
@render_time
def edit_profiles():
    user = g.user
    if not is_in_group(user, "administrators"):
        return render('edit_profiles.html', authn_error="only administrators can access this page.")
    if request.method == 'POST':
        user = User.query.filter_by(id=request.form["id"]).first()
        action = request.form["action"]
        if action == "save":
            user.name = request.form["name"]
            user.email = request.form["email"]
            user.openid = request.form["openid"]
            user.groups_string = request.form["groups_string"]
        elif action == "delete":
            db_session.delete(user)
        else:
            raise Exception("Unknown profile editing action: \"%s\"" %action)
        db_session.commit()
    profiles = User.query.all()
    return render('edit_profiles.html', profiles=profiles)

@app.route('/config', methods=['GET', 'POST'])
@render_time
def edit_config():
    user = g.user
    if not is_in_group(user, "administrators"):
        return render('edit_config.html', authn_error=True)
    def get_rows(textarea):
        return len(textarea.split("\n")) * 1.2
    def format_json(json_string):
        return json.dumps(json_string, indent=4, sort_keys=True, separators=(",", " : "))

    config = format_json(cfg())
    if request.method == 'POST':
        action = request.form["action"]
        if action == "save":
            config = request.form["config"]
            try:
                tmp = json.loads(config)
                config = format_json(tmp)
            except ValueError, ve:
                return render('edit_config.html', config=config, rows=get_rows(config), error="configuration syntax error.")
            with open("config.json", "w") as f:
                f.write(config)
                f.flush()
        elif action == "revert":
            pass
        else:
            raise Exception("Unknown config editing action: \"%s\"" %action)
    return render('edit_config.html', config=config, rows=get_rows(config))

@app.route('/profile', methods=['GET', 'POST'])
@render_time
def edit_profile():
    user = g.user
    if user is None:
        return render('edit_profile.html', authn_error="only logged in users can edit their profile")
    form = dict(name=user.name, email=user.email)
    if request.method == 'POST':
        if 'delete' in request.form:
            db_session.delete(user)
            db_session.commit()
            session['openid'] = None
            flash(u'Profile deleted')
            return redirect(url_for('index'))
        form['name'] = request.form['name']
        form['email'] = request.form['email']
        if not form['name']:
            flash(u'Error: you have to provide a name')
        elif '@' not in form['email']:
            flash(u'Error: you have to enter a valid email address')
        else:
            flash(u'Profile successfully created')
            user.name = form['name']
            user.email = form['email']
            db_session.commit()
            return redirect(url_for('edit_profile', user=user))
    return render('edit_profile.html', form=form)

@app.route('/logout')
def logout():
    session.pop('openid', None)
    flash(u'You have been signed out')
    return redirect(oid.get_next_url())

def set_access_groups(path, groups):
    """ set groups that are authorized to read the specified directory """
    with open(os.path.join(path, ".access"), "w") as f:
        f.write("\n".join(groups))
        f.flush()
def get_access_groups(path):
    """ get groups that are authorized to read the specified directory """
    try:
        with open(os.path.join(path, ".access"), "r") as f:
            return f.read().split()
    except IOError:
        return []

def access_permitted(allowed_groups, user_groups):
    """ check if any of the user groups is in the list of allowed groups """
    return bool(set(allowed_groups).intersection(set(user_groups)))

def get_route(path):
    route = []
    paths = []
    temp_paths = path.split(os.sep)
    for k,v in enumerate(temp_paths):
        cur_path = temp_paths[:k+1]
        paths.append(os.path.join(*cur_path).decode("utf-8"))
    for p in paths:
        route.append( {"key": p, "value": os.path.basename(p)} )
    return route

@app.route('/gallery/<path:path>', methods=['GET', 'POST'])
@render_time
def show_gallery(path):
    global g
    user = g.user
    if user is None and not cfg()["public_access"]:
        return render("gallery.html", authn_error="only logged in users may view this page")
    path = urllib.unquote(path).encode("utf-8")
    check_jailed_path(path, "data")
    gallery_groups = get_access_groups(path.encode("utf-8"))
    if not cfg()["public_access"]:
        if not access_permitted(gallery_groups, user.get_groups()):
            return render("gallery.html", authn_error="not enough permissions to view this page")

    gallery = Gallery(path, follow_freedesktop_standard = cfg()["follow_freedesktop_standard"])
    gallery.populate()
    if not cfg()["public_access"]:
        galleries = [gal for gal in gallery.get_galleries() if access_permitted(get_access_groups(gal["key"].encode("utf-8")), user.get_groups())]
    else:
        galleries = gallery.get_galleries()
    groups = None
    groups_error = None
    if request.method == 'POST':
        action = request.form["action"]
        if action == "save":
            gallery_groups = request.form["groups_string"].split()
            try:
                set_access_groups(path.encode("utf-8"), gallery_groups)
            except IOError, ioe:
                groups_error = "%s" %ioe
        else:
            raise Exception("Unknown gallery editing action: \"%s\"" %action)

    if not is_in_group(user, "administrators"):
        groups = " ".join(gallery_groups)
    return render("gallery.html", path = gallery.path.decode("utf-8"), route = get_route(gallery.path)[1:], galleries = galleries, files = gallery.get_files(), groups=groups, groups_error=groups_error)

@app.route('/video/<path:path>')
@render_time
def show_video(path):
    global g
    user = g.user
    if user is None and not cfg()["public_access"]:
        return render("video.html", authn_error="only logged in users may view this page")
    video_path = urllib.unquote(path).encode("utf-8")
    path = os.path.dirname(video_path)
    check_jailed_path(path, "data")
    return render("video.html", video_path = video_path, route=get_route(video_path)[1:-1], video_basename = os.path.basename(video_path))

def check_jailed_path(path, jail_path):
    if os.path.normpath(path) == jail_path:
        return
    if not os.path.normpath(path).startswith(jail_path + os.sep):
        raise Exception("Permission denied, '%s' is outside '%s'" %(path, jail_path))

@app.route('/data/<path:path>')
def show_data(path):
    global g
    user = g.user
    if user is None and not cfg()["public_access"]:
        abort(401)
    path = urllib.unquote(path).encode("utf-8")
    if os.path.basename(path) == ".access":
        abort(401)
    check_jailed_path(path, "data")
    return send_file(path)

@app.route('/static/<path:path>')
def show_static(path):
    path = os.path.join("static", urllib.unquote(path).encode("utf-8"))
    check_jailed_path(path, "static")
    return send_file(path)

if __name__ == '__main__':
    app.run(debug=cfg()["debug_mode_enabled"], host="0.0.0.0")
<commit_msg>Remove unnecessary error information.<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2013, Bruno Gonzalez <stenyak@stenyak.com>. This software is licensed under the Affero General Public License version 3 or later.  See the LICENSE file.

import os, urllib, time
import json
from functools import wraps

from flask import request, g, session, flash, url_for, abort, Flask, redirect, send_file, render_template
from flask_openid import OpenID
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

from gallery import Gallery
from config import get_config as cfg

def init_globals(name, db_uri):
    def init_flask_openid(app):
        return OpenID(app)
    def init_flask(name, db_uri):
        app = Flask(name)
        app.config.update(DATABASE_URI=db_uri, SECRET_KEY='development key', DEBUG=True)
        return app
    def init_sqlalchemy(db_uri):
        engine = create_engine(db_uri)
        db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
        base = declarative_base()
        base.query = db_session.query_property()
        return engine, base, db_session
    app = init_flask(name, db_uri)
    oid = init_flask_openid(app)
    engine, base, db_session = init_sqlalchemy(db_uri)
    return app, oid, engine, base, db_session

db_uri = "sqlite:///" + "profile.db"
app, oid, engine, Base, db_session = init_globals(__name__, db_uri)

def init_db():
    global engine
    global Base
    Base.metadata.create_all(bind=engine)

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(60))
    email = Column(String(200))
    openid = Column(String(200))
    groups_string = Column(String(200))

    def __init__(self, name, email, openid, groups_string):
        self.name = name
        self.email = email
        self.openid = openid
        self.groups_string = groups_string
    def get_groups(self):
        return self.groups_string.split()

def db_created():
    try:
        User.query.filter_by(openid="foobar").first()
        return True
    except:
        return False

def db_required(fn):
    @wraps(fn)
    def inner(*args, **kwargs):
        if not db_created():
            init_db()
        return fn(*args, **kwargs)
    return inner

def render_time(fn):
    @wraps(fn)
    def inner(*args, **kwargs):
        start = time.time()
        result = fn(*args, **kwargs)
        end = time.time()
        try:
            return result.replace("TTTTIME", "%.4f" %(end-start))
        except AttributeError:
            return result
    return inner

@app.before_request
@db_required
def before_request():
    g.user = None
    if 'openid' in session:
        g.user = User.query.filter_by(openid=session['openid']).first()

@app.after_request
def after_request(response):
    db_session.remove()
    return response

@app.route('/favicon.ico')
def favicon():
    return send_file("static/favicon.ico")

@app.route('/')
def index():
    return redirect("/gallery/data")

def get_openid_providers():
    class OpenIdProvider():
        def __init__(self, name, url, hidden=True):
            self.name = name
            self.url = url
            self.image = "openid/" + name.replace(" ", "").lower() + ".png"
            self.hidden = hidden
    return [
    OpenIdProvider("Google", "https://www.google.com/accounts/o8/id"),
    OpenIdProvider("Yahoo", "yahoo.com"),
    OpenIdProvider("WordPress", "Your WordPress blog url", False),
    OpenIdProvider("Flickr", "flickr.com"),
    OpenIdProvider("Stack Exchange", "https://openid.stackexchange.com"),
    OpenIdProvider("Blogger", "Your Blogger blog url", False),
    OpenIdProvider("LiveJournal", "Your LiveJournal blog url", False),
    OpenIdProvider("OpenID", "Your custom OpenID url", False)
    ]

def is_in_group(user, group):
    return user is not None and group in user.get_groups()

def render(*args, **kwargs):
    global g
    user = g.user
    return render_template(*args, debug=cfg()["debug_mode_enabled"], user=user, **kwargs)

@app.route('/login', methods=['GET', 'POST'])
@render_time
@oid.loginhandler
def login():
    """Does the login via OpenID.  Has to call into `oid.try_login` to start the OpenID machinery.  """
    # if we are already logged in, go back to were we came from
    if g.user is not None:
        return redirect(oid.get_next_url())
    if request.method == 'POST':
        openid = request.form.get('openid')
        if openid:
            return oid.try_login(openid, ask_for=['email', 'fullname', 'nickname'])
    return render('login.html', next=oid.get_next_url(), error=oid.fetch_error(), providers=get_openid_providers())

@db_required
@oid.after_login
def create_or_login(resp):
    """This is called when login with OpenID succeeded and it's not necessary to figure out if this is the users's first login or not.  This function has to redirect otherwise the user will be presented with a terrible URL which we certainly don't want.  """
    session['openid'] = resp.identity_url
    user = User.query.filter_by(openid=resp.identity_url).first()
    if user is not None:
        flash(u'Successfully signed in')
        g.user = user
        return redirect(oid.get_next_url())
    return redirect(url_for('create_profile', next=oid.get_next_url(), name=resp.fullname or resp.nickname, email=resp.email))

@app.route('/create-profile', methods=['GET', 'POST'])
@render_time
def create_profile():
    """If this is the user's first login, the create_or_login function will redirect here so that the user can set up his profile.  """
    if g.user is not None or 'openid' not in session:
        return redirect(url_for('index'))
    if not cfg()["profile_creation_enabled"]:
        return render('create_profile.html', authn_error="profile creation has been disabled by the administrator.")
    if request.method == 'POST':
        name = request.form['name']
        email = request.form['email']
        if not name:
            flash(u'Error: you have to provide a name')
        elif '@' not in email:
            flash(u'Error: you have to enter a valid email address')
        else:
            flash(u'Profile successfully created')
            groups_string = ""
            if len(User.query.all()) == 0:
                groups_string = "administrators"
            db_session.add(User(name, email, session['openid'], groups_string))
            db_session.commit()
            return redirect(oid.get_next_url())
    return render('create_profile.html', next_url=oid.get_next_url())

@app.route('/profiles', methods=['GET', 'POST'])
@render_time
def edit_profiles():
    user = g.user
    if not is_in_group(user, "administrators"):
        return render('edit_profiles.html', authn_error=True)
    if request.method == 'POST':
        user = User.query.filter_by(id=request.form["id"]).first()
        action = request.form["action"]
        if action == "save":
            user.name = request.form["name"]
            user.email = request.form["email"]
            user.openid = request.form["openid"]
            user.groups_string = request.form["groups_string"]
        elif action == "delete":
            db_session.delete(user)
        else:
            raise Exception("Unknown profile editing action: \"%s\"" %action)
        db_session.commit()
    profiles = User.query.all()
    return render('edit_profiles.html', profiles=profiles)

@app.route('/config', methods=['GET', 'POST'])
@render_time
def edit_config():
    user = g.user
    if not is_in_group(user, "administrators"):
        return render('edit_config.html', authn_error=True)
    def get_rows(textarea):
        return len(textarea.split("\n")) * 1.2
    def format_json(json_string):
        return json.dumps(json_string, indent=4, sort_keys=True, separators=(",", " : "))

    config = format_json(cfg())
    if request.method == 'POST':
        action = request.form["action"]
        if action == "save":
            config = request.form["config"]
            try:
                tmp = json.loads(config)
                config = format_json(tmp)
            except ValueError, ve:
                return render('edit_config.html', config=config, rows=get_rows(config), error="configuration syntax error.")
            with open("config.json", "w") as f:
                f.write(config)
                f.flush()
        elif action == "revert":
            pass
        else:
            raise Exception("Unknown config editing action: \"%s\"" %action)
    return render('edit_config.html', config=config, rows=get_rows(config))

@app.route('/profile', methods=['GET', 'POST'])
@render_time
def edit_profile():
    user = g.user
    if user is None:
        return render('edit_profile.html', authn_error="only logged in users can edit their profile")
    form = dict(name=user.name, email=user.email)
    if request.method == 'POST':
        if 'delete' in request.form:
            db_session.delete(user)
            db_session.commit()
            session['openid'] = None
            flash(u'Profile deleted')
            return redirect(url_for('index'))
        form['name'] = request.form['name']
        form['email'] = request.form['email']
        if not form['name']:
            flash(u'Error: you have to provide a name')
        elif '@' not in form['email']:
            flash(u'Error: you have to enter a valid email address')
        else:
            flash(u'Profile successfully created')
            user.name = form['name']
            user.email = form['email']
            db_session.commit()
            return redirect(url_for('edit_profile', user=user))
    return render('edit_profile.html', form=form)

@app.route('/logout')
def logout():
    session.pop('openid', None)
    flash(u'You have been signed out')
    return redirect(oid.get_next_url())

def set_access_groups(path, groups):
    """ set groups that are authorized to read the specified directory """
    with open(os.path.join(path, ".access"), "w") as f:
        f.write("\n".join(groups))
        f.flush()
def get_access_groups(path):
    """ get groups that are authorized to read the specified directory """
    try:
        with open(os.path.join(path, ".access"), "r") as f:
            return f.read().split()
    except IOError:
        return []

def access_permitted(allowed_groups, user_groups):
    """ check if any of the user groups is in the list of allowed groups """
    return bool(set(allowed_groups).intersection(set(user_groups)))

def get_route(path):
    route = []
    paths = []
    temp_paths = path.split(os.sep)
    for k,v in enumerate(temp_paths):
        cur_path = temp_paths[:k+1]
        paths.append(os.path.join(*cur_path).decode("utf-8"))
    for p in paths:
        route.append( {"key": p, "value": os.path.basename(p)} )
    return route

@app.route('/gallery/<path:path>', methods=['GET', 'POST'])
@render_time
def show_gallery(path):
    global g
    user = g.user
    if user is None and not cfg()["public_access"]:
        return render("gallery.html", authn_error="only logged in users may view this page")
    path = urllib.unquote(path).encode("utf-8")
    check_jailed_path(path, "data")
    gallery_groups = get_access_groups(path.encode("utf-8"))
    if not cfg()["public_access"]:
        if not access_permitted(gallery_groups, user.get_groups()):
            return render("gallery.html", authn_error=True)

    gallery = Gallery(path, follow_freedesktop_standard = cfg()["follow_freedesktop_standard"])
    gallery.populate()
    if not cfg()["public_access"]:
        galleries = [gal for gal in gallery.get_galleries() if access_permitted(get_access_groups(gal["key"].encode("utf-8")), user.get_groups())]
    else:
        galleries = gallery.get_galleries()
    groups = None
    groups_error = None
    if request.method == 'POST':
        action = request.form["action"]
        if action == "save":
            gallery_groups = request.form["groups_string"].split()
            try:
                set_access_groups(path.encode("utf-8"), gallery_groups)
            except IOError, ioe:
                groups_error = "%s" %ioe
        else:
            raise Exception("Unknown gallery editing action: \"%s\"" %action)

    if not is_in_group(user, "administrators"):
        groups = " ".join(gallery_groups)
    return render("gallery.html", path = gallery.path.decode("utf-8"), route = get_route(gallery.path)[1:], galleries = galleries, files = gallery.get_files(), groups=groups, groups_error=groups_error)

@app.route('/video/<path:path>')
@render_time
def show_video(path):
    global g
    user = g.user
    if user is None and not cfg()["public_access"]:
        return render("video.html", authn_error="only logged in users may view this page")
    video_path = urllib.unquote(path).encode("utf-8")
    path = os.path.dirname(video_path)
    check_jailed_path(path, "data")
    return render("video.html", video_path = video_path, route=get_route(video_path)[1:-1], video_basename = os.path.basename(video_path))

def check_jailed_path(path, jail_path):
    if os.path.normpath(path) == jail_path:
        return
    if not os.path.normpath(path).startswith(jail_path + os.sep):
        raise Exception("Permission denied, '%s' is outside '%s'" %(path, jail_path))

@app.route('/data/<path:path>')
def show_data(path):
    global g
    user = g.user
    if user is None and not cfg()["public_access"]:
        abort(401)
    path = urllib.unquote(path).encode("utf-8")
    if os.path.basename(path) == ".access":
        abort(401)
    check_jailed_path(path, "data")
    return send_file(path)

@app.route('/static/<path:path>')
def show_static(path):
    path = os.path.join("static", urllib.unquote(path).encode("utf-8"))
    check_jailed_path(path, "static")
    return send_file(path)

if __name__ == '__main__':
    app.run(debug=cfg()["debug_mode_enabled"], host="0.0.0.0")
<|endoftext|>


<commit_before>import re
import subprocess
from syncplay.players.mplayer import MplayerPlayer
from syncplay.messages import getMessage
from syncplay import constants
import os, sys, time

class MpvPlayer(MplayerPlayer):
    POSITION_QUERY = 'time-pos'
    OSD_QUERY = 'show_text'
    RE_VERSION = re.compile('.*mpv (\d)\.(\d)\.\d.*')

    def _setProperty(self, property_, value):
        self._listener.sendLine("no-osd set {} {}".format(property_, value))

    def setPaused(self, value):
        if self._paused <> value:
            self._paused = not self._paused
            self._listener.sendLine('cycle pause')

    def _storePosition(self, value):
        if self._resetPosition:
            self._position = 0
        elif self._fileIsLoaded():
            self._position = value
        else:
            self._position = self._client.getGlobalPosition()

    def _storePauseState(self, value):
        if self._fileIsLoaded():
            self._paused = value
        else:
            self._paused = self._client.getGlobalPaused()

    def _onFileUpdate(self):
        pass

    def _clearFileLoaded(self):
        self.fileLoaded = False
        self.lastLoadedTime = None

    def _loadFile(self, filePath):
        self._clearFileLoaded()
        self._listener.sendLine(u'loadfile {}'.format(self._quoteArg(filePath)))

    def _handleUnknownLine(self, line):
        if "Error parsing option" in line:
            self.quitReason = getMessage("mpv-version-error")

        elif line == "<SyncplayUpdateFile>":
            self._clearFileLoaded()

        elif line == "</SyncplayUpdateFile>":
            self._onMPVFileUpdate()

        elif "Failed to get value of property" in line:
            if "filename" in line:
                self._getFilename()
            elif "length" in line:
                self._getLength()
            elif "path" in line:
                self._getFilepath()
            elif "time-pos" in line:
                self.setPosition(self._client.getGlobalPosition())
                self._positionAsk.set()

        elif "Playing:" in line:
            if self._position < 1:
                self._resetPosition = True
            self._clearFileLoaded()

    def _onMPVFileUpdate(self):
        self.fileLoaded = True
        self.lastLoadedTime = time.time()
        self.reactor.callFromThread(self._client.updateFile, self._filename, self._duration, self._filepath)
        if not self._resetPosition:
            self.reactor.callFromThread(self.setPosition, self._client.getGlobalPosition())
        self._resetPosition = False
        if self._paused != self._client.getGlobalPaused():
            self.reactor.callFromThread(self._client.getGlobalPaused)

    def _fileIsLoaded(self):
        if self.fileLoaded == True and self.lastLoadedTime != None and time.time() > (self.lastLoadedTime + constants.MPV_NEWFILE_IGNORE_TIME):
            return True
        else:
            return False

    @staticmethod
    def run(client, playerPath, filePath, args):
        return MpvPlayer(client, MpvPlayer.getExpandedPath(playerPath), filePath, args)

    @staticmethod
    def getStartupArgs(path):
        ver = MpvPlayer.RE_VERSION.search(subprocess.check_output([path, '--version']))
        new_mpv = ver is None or int(ver.group(1)) > 0 or int(ver.group(2)) >= 5
        args = constants.MPV_SLAVE_ARGS
        if sys.platform.startswith('win') or not new_mpv:
             args.extend(constants.MPV_SLAVE_ARGS_WINDOWS)
        else:
             args.extend(constants.MPV_SLAVE_ARGS_NONWINDOWS)
        return args

    @staticmethod
    def getDefaultPlayerPathsList():
        l = []
        for path in constants.MPV_PATHS:
            p = MpvPlayer.getExpandedPath(path)
            if p:
                l.append(p)
        return l

    @staticmethod
    def isValidPlayerPath(path):
        if "mpv" in path and MpvPlayer.getExpandedPath(path):
            return True
        return False

    @staticmethod
    def getExpandedPath(playerPath):
        if not os.path.isfile(playerPath):
            if os.path.isfile(playerPath + u"mpv.exe"):
                playerPath += u"mpv.exe"
                return playerPath
            elif os.path.isfile(playerPath + u"\\mpv.exe"):
                playerPath += u"\\mpv.exe"
                return playerPath
        if os.access(playerPath, os.X_OK):
            return playerPath
        for path in os.environ['PATH'].split(':'):
            path = os.path.join(os.path.realpath(path), playerPath)
            if os.access(path, os.X_OK):
                return path

    @staticmethod
    def getIconPath(path):
        return constants.MPV_ICONPATH
<commit_msg>mpv: Handle old vers / exceptions better<commit_after>import re
import subprocess
from syncplay.players.mplayer import MplayerPlayer
from syncplay.messages import getMessage
from syncplay import constants
import os, sys, time

class MpvPlayer(MplayerPlayer):
    POSITION_QUERY = 'time-pos'
    OSD_QUERY = 'show_text'
    RE_VERSION = re.compile('.*mpv (\d)\.(\d)\.\d.*')

    def _setProperty(self, property_, value):
        self._listener.sendLine("no-osd set {} {}".format(property_, value))

    def setPaused(self, value):
        if self._paused <> value:
            self._paused = not self._paused
            self._listener.sendLine('cycle pause')

    def _storePosition(self, value):
        if self._resetPosition:
            self._position = 0
        elif self._fileIsLoaded():
            self._position = value
        else:
            self._position = self._client.getGlobalPosition()

    def _storePauseState(self, value):
        if self._fileIsLoaded():
            self._paused = value
        else:
            self._paused = self._client.getGlobalPaused()

    def _onFileUpdate(self):
        pass

    def _clearFileLoaded(self):
        self.fileLoaded = False
        self.lastLoadedTime = None

    def _loadFile(self, filePath):
        self._clearFileLoaded()
        self._listener.sendLine(u'loadfile {}'.format(self._quoteArg(filePath)))

    def _handleUnknownLine(self, line):
        if "Error parsing option" in line or "Error parsing commandline option" in line:
            self.quitReason = getMessage("mpv-version-error")

        elif line == "<SyncplayUpdateFile>":
            self._clearFileLoaded()

        elif line == "</SyncplayUpdateFile>":
            self._onMPVFileUpdate()

        elif "Failed to get value of property" in line:
            if "filename" in line:
                self._getFilename()
            elif "length" in line:
                self._getLength()
            elif "path" in line:
                self._getFilepath()
            elif "time-pos" in line:
                self.setPosition(self._client.getGlobalPosition())
                self._positionAsk.set()

        elif "Playing:" in line:
            if self._position < 1:
                self._resetPosition = True
            self._clearFileLoaded()

    def _onMPVFileUpdate(self):
        self.fileLoaded = True
        self.lastLoadedTime = time.time()
        self.reactor.callFromThread(self._client.updateFile, self._filename, self._duration, self._filepath)
        if not self._resetPosition:
            self.reactor.callFromThread(self.setPosition, self._client.getGlobalPosition())
        self._resetPosition = False
        if self._paused != self._client.getGlobalPaused():
            self.reactor.callFromThread(self._client.getGlobalPaused)

    def _fileIsLoaded(self):
        if self.fileLoaded == True and self.lastLoadedTime != None and time.time() > (self.lastLoadedTime + constants.MPV_NEWFILE_IGNORE_TIME):
            return True
        else:
            return False

    @staticmethod
    def run(client, playerPath, filePath, args):
        return MpvPlayer(client, MpvPlayer.getExpandedPath(playerPath), filePath, args)

    @staticmethod
    def getStartupArgs(path):
        try:
            ver = MpvPlayer.RE_VERSION.search(subprocess.check_output([path, '--version']))
        except:
            ver = None
        new_mpv = ver is None or int(ver.group(1)) > 0 or int(ver.group(2)) >= 5
        args = constants.MPV_SLAVE_ARGS
        if sys.platform.startswith('win') or not new_mpv:
             args.extend(constants.MPV_SLAVE_ARGS_WINDOWS)
        else:
             args.extend(constants.MPV_SLAVE_ARGS_NONWINDOWS)
        return args

    @staticmethod
    def getDefaultPlayerPathsList():
        l = []
        for path in constants.MPV_PATHS:
            p = MpvPlayer.getExpandedPath(path)
            if p:
                l.append(p)
        return l

    @staticmethod
    def isValidPlayerPath(path):
        if "mpv" in path and MpvPlayer.getExpandedPath(path):
            return True
        return False

    @staticmethod
    def getExpandedPath(playerPath):
        if not os.path.isfile(playerPath):
            if os.path.isfile(playerPath + u"mpv.exe"):
                playerPath += u"mpv.exe"
                return playerPath
            elif os.path.isfile(playerPath + u"\\mpv.exe"):
                playerPath += u"\\mpv.exe"
                return playerPath
        if os.access(playerPath, os.X_OK):
            return playerPath
        for path in os.environ['PATH'].split(':'):
            path = os.path.join(os.path.realpath(path), playerPath)
            if os.access(path, os.X_OK):
                return path

    @staticmethod
    def getIconPath(path):
        return constants.MPV_ICONPATH
<|endoftext|>


<commit_before>#!/usr/bin/env python
"""
Rename files or directories using regular expression.
Does nothing without the --force option.

example:
    ./pyrename.py '(.*)\.py' '\g<1>_renamed.py'

"""
import itertools
import argparse
import logging
import shutil
import os
import re

def setup_logging():
    lfmt = '[%(asctime)s][%(process)5d][%(levelname)-5s]: %(message)s'
    dfmt = "%Y-%m-%d %H:%M:%S"
    logging.basicConfig(level=logging.INFO, format=lfmt, datefmt=dfmt)

setup_logging()

def log_option(opts, attr):
    obj = getattr(opts, attr)

    if isinstance(obj, list) or isinstance(obj, tuple):
        for i, item in enumerate(obj):
            logging.info('%-10s = %s', '{}[{:d}]'.format(attr, i), obj[i])
        return

    if isinstance(obj, set):
        i = 0
        for item in obj:
            logging.info('%-10s = %s', '{}[{:d}]'.format(attr, i), item)
            i += 1
        return

    logging.info('%-10s = %s', attr, obj)

class Options(object):
    def __init__(self, work):
        self.pattern = None
        self.replace = None
        self.nomatch = None
        self.exclude = []
        self.top     = os.path.realpath(os.path.expanduser(work))

        self.recursive  = False
        self.dirs       = False
        self.files      = True
        self.both       = False
        self.force      = False
        self.ignorecase = False
        self.silent     = False

def get_arguments(work, args=None):
    parser = argparse.ArgumentParser(description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter)
    opts = Options(work)

    # regular expressions
    parser.add_argument(dest='pattern', type=str,
        help='expression: pattern')
    parser.add_argument(dest='replace', type=str,
        help='expression: replace')
    parser.add_argument('--nomatch', type=str, default=opts.nomatch,
        help='expression: nomatch')
    parser.add_argument('--exclude', type=str, nargs='*', default=opts.exclude,
        help='list of files to exclude')

    # searching
    parser.add_argument('-t', '--top', type=str, default=opts.top,
        help='top level directory')
    parser.add_argument('-r', '--recursive', action='store_true',
        help='search recursivly')

    fgroup = parser.add_mutually_exclusive_group()
    fgroup.add_argument('-d', '--dirs' , action='store_true',
        help='match directories')
    fgroup.add_argument('-b', '--both' , action='store_true',
        help='match directories and files')

    # mode
    parser.add_argument('-f', '--force', action='store_true',
        help='rename files')
    parser.add_argument('-i', '--ignorecase', action='store_true',
        help='ignore case')
    parser.add_argument('-s', '--silent', action='store_true',
        help='silent')

    # parse
    parser.parse_args(args, namespace=opts)

    # logging
    if opts.silent:
        logging.getLogger().setLevel(logging.ERROR)

    # update opts
    if opts.dirs:
        opts.files = False
    if opts.both:
        opts.dirs  = True
        opts.files = True

    # fix path
    opts.top = os.path.realpath(opts.top)

    # fix exclude
    opts.exclude = set(opts.exclude)
    opts.exclude.add(os.path.basename(__file__))

    # log options
    log_option(opts, 'pattern')
    log_option(opts, 'replace')
    log_option(opts, 'nomatch')
    log_option(opts, 'top')

    log_option(opts, 'recursive')
    log_option(opts, 'files')
    log_option(opts, 'dirs')
    log_option(opts, 'both')

    log_option(opts, 'exclude')

    log_option(opts, 'force')
    log_option(opts, 'ignorecase')

    return opts

def walk(top, r=False, dirs=False, files=False):
    if dirs or files:
        if r:
            for root, dbases, fbases in os.walk(top, followlinks=False):
                if dirs and files:
                    for p in itertools.chain(dbases, fbases):
                        yield root, p
                elif dirs:
                    for d in dbases:
                        yield root, d
                elif files:
                    for f in fbases:
                        yield root, f
        else:
            root, dbases, fbases = next(os.walk(top, followlinks=False))
            if dirs and files:
                for p in itertools.chain(dbases, fbases):
                    yield root, p
            elif dirs:
                for d in dbases:
                    yield root, d
            elif files:
                for f in fbases:
                    yield root, f
    else:
        raise StopIteration

def main(args=None):
    work = os.getcwd()
    opts = get_arguments(work, args)

    # check top level directory
    if not os.path.exists(opts.top) or not os.path.isdir(opts.top):
        raise RuntimeError('invalid top level directory: %s' % opts.top)

    # compile regex
    if opts.ignorecase:
        regex1 = re.compile(opts.pattern, re.IGNORECASE)
        try:
            regex2 = re.compile(opts.nomatch, re.IGNORECASE)
        except TypeError:
            regex2 = None
    else:
        regex1 = re.compile(opts.pattern)
        try:
            regex2 = re.compile(opts.nomatch)
        except TypeError:
            regex2 = None

    # record errors
    error = False

    # find paths
    opaths = []
    npaths = []
    for root, p in walk(opts.top, r=opts.recursive, dirs=opts.dirs, files=opts.files):
        match = regex1.match(p)
        if match:
            # exclude list
            if p in opts.exclude:
                logging.info('path excluded!\n\n\t%s\n', os.path.join(root, p))
                continue

            # exclude nomatch
            if not regex2 is None and regex2.match(p):
                logging.info('path excluded!\n\n\t%s\n', os.path.join(root, p))
                continue

            # construct new base
            try:
                n = regex1.sub(opts.replace, p)
            except re.error:
                logging.exception('regex error')
                error = True
                n = p

            # construct paths
            opath = os.path.join(root, p)
            npath = os.path.join(root, n)
            opaths.append((root, p, opath))
            npaths.append((root, n, npath))

            # output match
            logging.info('found a match!\n\n\topath (%d): %s\n\tnpath (%d): %s\n',
                os.path.exists(opath), opath, os.path.exists(npath), npath)

    # descibe paths
    oset = set(opaths)
    nset = set(npaths)
    iset = oset.intersection(nset)

    logging.info('%d old', len(opaths))
    logging.info('%d old (unique)', len(oset))
    logging.info('%d new', len(npaths))
    logging.info('%d new (unique)', len(nset))
    logging.info('%d same', len(iset))

    # make sure paths were found
    try:
        assert opaths
    except AssertionError:
        logging.error('no old paths found')
        error = True

    # make sure paths were found
    try:
        assert npaths
    except AssertionError:
        logging.error('no new paths found')
        error = True

    # make sure old paths are unique
    try:
        assert len(oset) is len(opaths)
    except AssertionError:
        logging.error('old paths are not unique')
        error = True

    # make sure new paths are unique
    try:
        assert len(nset) is len(npaths)
    except AssertionError:
        logging.error('new paths are not unique')
        error = True

    # make sure old paths and new paths do not intersect
    try:
        assert not iset
    except AssertionError:
        logging.error('some paths are the same')
        error = True

    # check if old paths exist
    try:
        for root, base, path in opaths:
            assert os.path.exists(path)
    except AssertionError:
        logging.error('some old paths do not exist')
        error = True

    # check if new paths exist
    try:
        for root, base, path in npaths:
           assert not os.path.exists(path)
    except AssertionError:
        logging.error('some new paths already exist')
        error = True

    # stop if there were errors
    if error:
        raise RuntimeError('invalid configuration')

    # move files
    if opts.force:
        logging.info('moving paths!')
        for (oroot, obase, opath), (nroot, nbase, npath) in zip(opaths, npaths):
            logging.info('\n\n\tmv %s\n\t   %s\n', opath, npath)
            shutil.move(opath, npath)
            pass
    else:
        logging.info('\n\n\tThis was a dry run, please use --force to perform renaming\n')

if __name__ == '__main__':
    try:
        main()
    except SystemExit:
        pass
    except:
        logging.exception('caught unhandled exception')
<commit_msg>Add extra output for path checking.<commit_after>#!/usr/bin/env python
"""
Rename files or directories using regular expression.
Does nothing without the --force option.

example:
    ./pyrename.py '(.*)\.py' '\g<1>_renamed.py'

"""
import itertools
import argparse
import logging
import shutil
import os
import re

def setup_logging():
    lfmt = '[%(asctime)s][%(process)5d][%(levelname)-5s]: %(message)s'
    dfmt = "%Y-%m-%d %H:%M:%S"
    logging.basicConfig(level=logging.INFO, format=lfmt, datefmt=dfmt)

setup_logging()

def log_option(opts, attr):
    obj = getattr(opts, attr)

    if isinstance(obj, list) or isinstance(obj, tuple):
        for i, item in enumerate(obj):
            logging.info('%-10s = %s', '{}[{:d}]'.format(attr, i), obj[i])
        return

    if isinstance(obj, set):
        i = 0
        for item in obj:
            logging.info('%-10s = %s', '{}[{:d}]'.format(attr, i), item)
            i += 1
        return

    logging.info('%-10s = %s', attr, obj)

class Options(object):
    def __init__(self, work):
        self.pattern = None
        self.replace = None
        self.nomatch = None
        self.exclude = []
        self.top     = os.path.realpath(os.path.expanduser(work))

        self.recursive  = False
        self.dirs       = False
        self.files      = True
        self.both       = False
        self.force      = False
        self.ignorecase = False
        self.silent     = False

def get_arguments(work, args=None):
    parser = argparse.ArgumentParser(description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter)
    opts = Options(work)

    # regular expressions
    parser.add_argument(dest='pattern', type=str,
        help='expression: pattern')
    parser.add_argument(dest='replace', type=str,
        help='expression: replace')
    parser.add_argument('--nomatch', type=str, default=opts.nomatch,
        help='expression: nomatch')
    parser.add_argument('--exclude', type=str, nargs='*', default=opts.exclude,
        help='list of files to exclude')

    # searching
    parser.add_argument('-t', '--top', type=str, default=opts.top,
        help='top level directory')
    parser.add_argument('-r', '--recursive', action='store_true',
        help='search recursivly')

    fgroup = parser.add_mutually_exclusive_group()
    fgroup.add_argument('-d', '--dirs' , action='store_true',
        help='match directories')
    fgroup.add_argument('-b', '--both' , action='store_true',
        help='match directories and files')

    # mode
    parser.add_argument('-f', '--force', action='store_true',
        help='rename files')
    parser.add_argument('-i', '--ignorecase', action='store_true',
        help='ignore case')
    parser.add_argument('-s', '--silent', action='store_true',
        help='silent')

    # parse
    parser.parse_args(args, namespace=opts)

    # logging
    if opts.silent:
        logging.getLogger().setLevel(logging.ERROR)

    # update opts
    if opts.dirs:
        opts.files = False
    if opts.both:
        opts.dirs  = True
        opts.files = True

    # fix path
    opts.top = os.path.realpath(opts.top)

    # fix exclude
    opts.exclude = set(opts.exclude)
    opts.exclude.add(os.path.basename(__file__))

    # log options
    log_option(opts, 'pattern')
    log_option(opts, 'replace')
    log_option(opts, 'nomatch')
    log_option(opts, 'top')

    log_option(opts, 'recursive')
    log_option(opts, 'files')
    log_option(opts, 'dirs')
    log_option(opts, 'both')

    log_option(opts, 'exclude')

    log_option(opts, 'force')
    log_option(opts, 'ignorecase')

    return opts

def walk(top, r=False, dirs=False, files=False):
    if dirs or files:
        if r:
            for root, dbases, fbases in os.walk(top, followlinks=False):
                if dirs and files:
                    for p in itertools.chain(dbases, fbases):
                        yield root, p
                elif dirs:
                    for d in dbases:
                        yield root, d
                elif files:
                    for f in fbases:
                        yield root, f
        else:
            root, dbases, fbases = next(os.walk(top, followlinks=False))
            if dirs and files:
                for p in itertools.chain(dbases, fbases):
                    yield root, p
            elif dirs:
                for d in dbases:
                    yield root, d
            elif files:
                for f in fbases:
                    yield root, f
    else:
        raise StopIteration

def main(args=None):
    work = os.getcwd()
    opts = get_arguments(work, args)

    # check top level directory
    if not os.path.exists(opts.top) or not os.path.isdir(opts.top):
        raise RuntimeError('invalid top level directory: %s' % opts.top)

    # compile regex
    if opts.ignorecase:
        regex1 = re.compile(opts.pattern, re.IGNORECASE)
        try:
            regex2 = re.compile(opts.nomatch, re.IGNORECASE)
        except TypeError:
            regex2 = None
    else:
        regex1 = re.compile(opts.pattern)
        try:
            regex2 = re.compile(opts.nomatch)
        except TypeError:
            regex2 = None

    # record errors
    error = False

    # find paths
    opaths = []
    npaths = []
    for root, p in walk(opts.top, r=opts.recursive, dirs=opts.dirs, files=opts.files):
        match = regex1.match(p)
        if match:
            # exclude list
            if p in opts.exclude:
                logging.info('path excluded!\n\n\t%s\n', os.path.join(root, p))
                continue

            # exclude nomatch
            if not regex2 is None and regex2.match(p):
                logging.info('path excluded!\n\n\t%s\n', os.path.join(root, p))
                continue

            # construct new base
            try:
                n = regex1.sub(opts.replace, p)
            except re.error:
                logging.exception('regex error')
                error = True
                n = p

            # construct paths
            opath = os.path.join(root, p)
            npath = os.path.join(root, n)
            opaths.append((root, p, opath))
            npaths.append((root, n, npath))

            # output match
            logging.info('found a match!\n\n\topath (%d): %s\n\tnpath (%d): %s\n',
                os.path.exists(opath), opath, os.path.exists(npath), npath)

    # descibe paths
    oset = set(opaths)
    nset = set(npaths)
    iset = oset.intersection(nset)

    logging.info('%d old', len(opaths))
    logging.info('%d old (unique)', len(oset))
    logging.info('%d new', len(npaths))
    logging.info('%d new (unique)', len(nset))
    logging.info('%d same', len(iset))

    # make sure paths were found
    try:
        assert opaths
    except AssertionError:
        logging.error('no old paths found')
        error = True

    # make sure paths were found
    try:
        assert npaths
    except AssertionError:
        logging.error('no new paths found')
        error = True

    # make sure old paths are unique
    try:
        assert len(oset) is len(opaths)
    except AssertionError:
        logging.error('old paths are not unique')
        error = True

    # make sure new paths are unique
    try:
        assert len(nset) is len(npaths)
    except AssertionError:
        logging.error('new paths are not unique')
        error = True

    # make sure old paths and new paths do not intersect
    try:
        assert not iset
    except AssertionError:
        logging.error('some paths are the same')
        error = True

    # check if old paths exist
    found = []
    for root, base, path in opaths:
        try:
            assert os.path.exists(path)
        except AssertionError:
            found.append(path)
    if found:
        logging.error('some old paths do not exist\n\n\t%s\n',
            '\n\t'.join(found))
        error = True

    # check if new paths exist
    found = []
    for root, base, path in npaths:
        try:
            assert not os.path.exists(path)
        except AssertionError:
            found.append(path)
    if found:
        logging.error('some new paths already exist\n\n\t%s\n',
            '\n\t'.join(found))
        error = True

    # stop if there were errors
    if error:
        raise RuntimeError('invalid configuration')

    # move files
    if opts.force:
        logging.info('moving paths!')
        for (oroot, obase, opath), (nroot, nbase, npath) in zip(opaths, npaths):
            logging.info('\n\n\tmv %s\n\t   %s\n', opath, npath)
            shutil.move(opath, npath)
            pass
    else:
        logging.info('\n\n\tThis was a dry run, please use --force to perform renaming\n')

if __name__ == '__main__':
    try:
        main()
    except SystemExit:
        pass
    except:
        logging.exception('caught unhandled exception')
<|endoftext|>


<commit_before># Python Security Project (PySec) and its related class files.
#
# PySec is a set of tools for secure application development under Linux
#
# Copyright 2014 PySec development team
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# -*- coding: ascii -*-
"""Traceback utilities."""
import keyword
import tokenize
import sys

from pysec.core import Object
from pysec.xsplit import xlines

NORESULT = object()

SCOPE_BUILTIN = 'builtin'
SCOPE_GLOBAL = 'global'
SCOPE_LOCAL = 'local'

def getvar(name, frame=None):
    """Search a name in local scope, global scope, and in builtins"""
    if frame is None:
        frame = sys._getframe().f_back
    val = frame.f_locals.get(name, NORESULT)
    if val is not NORESULT:
        return SCOPE_LOCAL, val
    val = frame.f_globals.get(name, NORESULT)
    if val is not NORESULT:
        return SCOPE_GLOBAL, val
    builtins = frame.f_globals.get('__builtins__', NORESULT)
    if builtins is not NORESULT:
        if type(builtins) is type({}):
            val = builtins.get(name, NORESULT)
            if val is not NORESULT:
                return SCOPE_BUILTIN, val
        else:
            val = getattr(builtins, name, NORESULT)
            if val is not NORESULT:
                return SCOPE_BUILTIN, val
    return None, None

class StringReadline(Object):

    def __init__(self, text):
        self.lines = xlines(text, eol='\n', keep_eol=1)

    def readline(self):
        try:
            return self.lines.next()
        except StopIteration:
            return ''

NOVAL = object()

def linevars(code, frame=None):
    if frame is None:
        frame = sys._getframe().f_back
    last = None
    parent = None
    prefix = ''
    value = NOVAL
    for tok_type, token, start, end, line in tokenize.generate_tokens(StringReadline(code)):
        if tok_type == tokenize.NEWLINE:
            break
        elif tok_type == tokenize.NAME and token not in keyword.kwlist:
            if last == '.':
                if parent is not NOVAL:
                    value = getattr(parent, token, NOVAL)
                    yield prefix + token, prefix, value
            else:
                where, value = getvar(token, frame)
                yield token, where, value
        elif token == '.':
            prefix = '%s%s.' % (prefix, last)
            parent = value
        else:
            parent = None
            prefix = ''
        last = token

class Hook(Object):

    def __init__(self, formatter, out=sys.stderr):
        self.out = out
        self.formatter = formatter

    def __call__(self, exc_type, exc_val, exc_tb):
        self.handle((exc_type, exc_val, exc_tb))

    def handle(self, info=None):
        self.out.write(self.formatter(info or sys.exc_info()))
        self.out.write('\n')

def reset_exceptook():
    sys.excepthook = sys.__excepthook__

<commit_msg>fix StringReadline use to tokenize code<commit_after># Python Security Project (PySec) and its related class files.
#
# PySec is a set of tools for secure application development under Linux
#
# Copyright 2014 PySec development team
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# -*- coding: ascii -*-
"""Traceback utilities."""
import keyword
import tokenize
import sys

from pysec.core import Object
from pysec.xsplit import xlines

NORESULT = object()

SCOPE_BUILTIN = 'builtin'
SCOPE_GLOBAL = 'global'
SCOPE_LOCAL = 'local'

def getvar(name, frame=None):
    """Search a name in local scope, global scope, and in builtins"""
    if frame is None:
        frame = sys._getframe().f_back
    val = frame.f_locals.get(name, NORESULT)
    if val is not NORESULT:
        return SCOPE_LOCAL, val
    val = frame.f_globals.get(name, NORESULT)
    if val is not NORESULT:
        return SCOPE_GLOBAL, val
    builtins = frame.f_globals.get('__builtins__', NORESULT)
    if builtins is not NORESULT:
        if type(builtins) is type({}):
            val = builtins.get(name, NORESULT)
            if val is not NORESULT:
                return SCOPE_BUILTIN, val
        else:
            val = getattr(builtins, name, NORESULT)
            if val is not NORESULT:
                return SCOPE_BUILTIN, val
    return None, None

class StringReadline(Object):

    def __init__(self, text):
        self.lines = xlines(text, eol='\n', keep_eol=1)

    def readline(self):
        try:
            return self.lines.next()
        except StopIteration:
            return ''

NOVAL = object()

def linevars(code, frame=None):
    if frame is None:
        frame = sys._getframe().f_back
    last = None
    parent = None
    prefix = ''
    value = NOVAL
    for tok_type, token, start, end, line in tokenize.generate_tokens(StringReadline(code).readline):
        if tok_type == tokenize.NEWLINE:
            break
        elif tok_type == tokenize.NAME and token not in keyword.kwlist:
            if last == '.':
                if parent is not NOVAL:
                    value = getattr(parent, token, NOVAL)
                    yield prefix + token, prefix, value
            else:
                where, value = getvar(token, frame)
                yield token, where, value
        elif token == '.':
            prefix = '%s%s.' % (prefix, last)
            parent = value
        else:
            parent = None
            prefix = ''
        last = token

class Hook(Object):

    def __init__(self, formatter, out=sys.stderr):
        self.out = out
        self.formatter = formatter

    def __call__(self, exc_type, exc_val, exc_tb):
        self.handle((exc_type, exc_val, exc_tb))

    def handle(self, info=None):
        self.out.write(self.formatter(info or sys.exc_info()))
        self.out.write('\n')

def reset_exceptook():
    sys.excepthook = sys.__excepthook__

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# pylint: disable=redefined-outer-name
import os
import random

import pytest
from raiden.network.utils import get_free_port

from raiden.utils import address_encoder, privatekey_to_address
from raiden.settings import (
    DEFAULT_EVENTS_POLL_TIMEOUT,
    DEFAULT_POLL_TIMEOUT,
    DEFAULT_PROTOCOL_THROTTLE_CAPACITY,
    DEFAULT_PROTOCOL_THROTTLE_FILL_RATE,
)
from raiden.network.transport import UDPTransport
from raiden.utils import sha3

# we need to use fixture for the default values otherwise
# pytest.mark.parametrize won't work (pytest 2.9.2)

@pytest.fixture
def settle_timeout(blockchain_type):
    """
    NettingChannel default settle timeout for tests.
    If using geth we set it considerably lower since waiting for
    too many blocks to be mined is very costly time-wise.
    """
    return 16

@pytest.fixture
def reveal_timeout(blockchain_type):
    """
    NettingChannel default reveal timeout for tests.
    If using geth we set it considerably lower since waiting for
    too many blocks to be mined is very costly time-wise.
    """
    return 4

@pytest.fixture
def events_poll_timeout():
    return DEFAULT_EVENTS_POLL_TIMEOUT

@pytest.fixture
def random_marker():
    """ A random marker used to identify a pytest run.

    Some tests will spawn a private chain, the private chain will be one or
    more ethereum nodes on a new subprocesss. These nodes may fail to start on
    concurrent test runs, mostly because of port number conflicts, but even
    though the test fails to start its private chain it may run interacting
    with the geth process from a different test run! This leads to
    unreasonable test errors.

    This fixture creates a random marker used to distinguish pytest runs and
    avoid test failures. Note this could fail for other reasons and fail to
    detect unwanted interations if the user sets the PYTHONHASHSEED to the same
    value.
    """
    random_hex = hex(random.getrandbits(100))

    # strip the leading 0x and trailing L
    return random_hex[2:-1]

@pytest.fixture
def deposit():
    """ Raiden chain default deposit. """
    # Arbitrary initial balance for each channel, using a small number for
    # easier calculations during testing
    return 200

@pytest.fixture
def both_participants_deposit():
    """ Boolean flag indicating if both participants of a channel put a deposit. """
    return True

@pytest.fixture
def number_of_tokens():
    """ Number of tokens pre-registered in the test Registry. """
    return 1

@pytest.fixture
def number_of_nodes():
    """ Number of raiden nodes in the test network. """
    return 3

@pytest.fixture
def channels_per_node():
    """ Number of pre-created channels per test raiden node. """
    return 1

@pytest.fixture
def poll_timeout():
    """ Timeout in seconds for polling a cluster. Used for geth. """
    return DEFAULT_POLL_TIMEOUT

@pytest.fixture
def transport_class():
    return UDPTransport

@pytest.fixture
def retry_interval():
    return 0.5

@pytest.fixture
def retries_before_backoff():
    return 2

@pytest.fixture
def throttle_capacity():
    return DEFAULT_PROTOCOL_THROTTLE_CAPACITY

@pytest.fixture
def throttle_fill_rate():
    return DEFAULT_PROTOCOL_THROTTLE_FILL_RATE

@pytest.fixture
def nat_invitation_timeout():
    return 5

@pytest.fixture
def nat_keepalive_retries():
    return 2

@pytest.fixture
def nat_keepalive_timeout():
    return 1

@pytest.fixture
def privatekey_seed(request):
    """ Private key template, allow different keys to be used for each test to
    avoid collisions.
    """
    return request.node.name + ':{}'

@pytest.fixture
def token_amount(number_of_nodes, deposit):
    total_per_node = 3 * (deposit + 1)
    total_token = total_per_node * number_of_nodes
    return total_token

@pytest.fixture
def network_wait():
    """Time is miliseconds used to wait for network events."""
    return 0.3

@pytest.fixture
def private_keys(number_of_nodes, privatekey_seed):
    """ Private keys for each raiden node. """

    # Note: The fixtures depend on the order of the private keys
    result = [
        sha3(privatekey_seed.format(position).encode())
        for position in range(number_of_nodes)
    ]

    # this must not happen, otherwise the keys and addresses will be equal!
    assert len(set(result)) == number_of_nodes, '`privatekey_seed` generate repeated keys'

    return result

@pytest.fixture
def deploy_key(privatekey_seed):
    return sha3(privatekey_seed.format('deploykey').encode())

@pytest.fixture
def blockchain_type(request):
    return request.config.option.blockchain_type

@pytest.fixture(autouse=True)
def blockchain_cache(request):
    """Expose flag --blockchain-cache value.

    This fixture allows tests to overwrite the blockchain_cache and force its
    value to True/False.
    """
    return request.config.option.blockchain_cache

@pytest.fixture
def blockchain_number_of_nodes():
    """ Number of nodes in the cluster, not the same as the number of raiden
    nodes. Used for all geth clusters and ignored for tester.
    """
    return 1

@pytest.fixture
def blockchain_key_seed():
    """ Private key template for the nodes in the private blockchain, allows
    different keys to be used for each test to avoid collisions.
    """
    return 'cluster:{}'

@pytest.fixture
def blockchain_private_keys(blockchain_number_of_nodes, blockchain_key_seed):
    """ The private keys for the each private chain node, not the same as the
    raiden's private key.
    """
    return [
        sha3(blockchain_key_seed.format(position).encode())
        for position in range(blockchain_number_of_nodes)
    ]

@pytest.fixture(scope='session')
def port_generator(request):
    """ count generator used to get a unique port number. """
    return get_free_port('127.0.0.1', request.config.option.initial_port)

@pytest.fixture
def blockchain_rpc_ports(blockchain_number_of_nodes, port_generator):
    """ A list of unique port numbers to be used by the blockchain nodes for
    the json-rpc interface.
    """
    return [
        next(port_generator)
        for _ in range(blockchain_number_of_nodes)
    ]

@pytest.fixture
def blockchain_p2p_ports(blockchain_number_of_nodes, port_generator):
    """ A list of unique port numbers to be used by the blockchain nodes for
    the p2p protocol.
    """
    return [
        next(port_generator)
        for _ in range(blockchain_number_of_nodes)
    ]

@pytest.fixture
def raiden_udp_ports(number_of_nodes, port_generator):
    """ A list of unique port numbers to be used by the raiden apps for the udp
    protocol.
    """
    return [
        next(port_generator)
        for _ in range(number_of_nodes)
    ]

@pytest.fixture
def rest_api_port_number(port_generator):
    """ Unique port for the REST API server. """
    return next(port_generator)

@pytest.fixture
def in_memory_database():
    """A boolean value indicating whether the sqlite3 databases will be in memory
    or in normal files. Defaults to True (in memory)."""
    return True

@pytest.fixture
def database_paths(tmpdir, private_keys, in_memory_database):
    """ Sqlite database paths for each app.
    """
    # According to http://www.sqlite.org/inmemorydb.html each memory connection will
    # create a unique in-memory DB, which is exactly what we need in this case for
    # each different Raiden app
    if in_memory_database:
        return [
            ':memory:'
            for position in range(len(private_keys))
        ]

    database_paths = list()
    for idx, pkey in enumerate(private_keys):
        app_dir = os.path.join(tmpdir.strpath, address_encoder(privatekey_to_address(pkey))[2:8])
        if not os.path.exists(app_dir):
            os.makedirs(app_dir)
        database_paths.append(os.path.join(app_dir, 'log.db'))

    return database_paths
<commit_msg>Fix network_wait fixture docstring<commit_after># -*- coding: utf-8 -*-
# pylint: disable=redefined-outer-name
import os
import random

import pytest
from raiden.network.utils import get_free_port

from raiden.utils import address_encoder, privatekey_to_address
from raiden.settings import (
    DEFAULT_EVENTS_POLL_TIMEOUT,
    DEFAULT_POLL_TIMEOUT,
    DEFAULT_PROTOCOL_THROTTLE_CAPACITY,
    DEFAULT_PROTOCOL_THROTTLE_FILL_RATE,
)
from raiden.network.transport import UDPTransport
from raiden.utils import sha3

# we need to use fixture for the default values otherwise
# pytest.mark.parametrize won't work (pytest 2.9.2)

@pytest.fixture
def settle_timeout(blockchain_type):
    """
    NettingChannel default settle timeout for tests.
    If using geth we set it considerably lower since waiting for
    too many blocks to be mined is very costly time-wise.
    """
    return 16

@pytest.fixture
def reveal_timeout(blockchain_type):
    """
    NettingChannel default reveal timeout for tests.
    If using geth we set it considerably lower since waiting for
    too many blocks to be mined is very costly time-wise.
    """
    return 4

@pytest.fixture
def events_poll_timeout():
    return DEFAULT_EVENTS_POLL_TIMEOUT

@pytest.fixture
def random_marker():
    """ A random marker used to identify a pytest run.

    Some tests will spawn a private chain, the private chain will be one or
    more ethereum nodes on a new subprocesss. These nodes may fail to start on
    concurrent test runs, mostly because of port number conflicts, but even
    though the test fails to start its private chain it may run interacting
    with the geth process from a different test run! This leads to
    unreasonable test errors.

    This fixture creates a random marker used to distinguish pytest runs and
    avoid test failures. Note this could fail for other reasons and fail to
    detect unwanted interations if the user sets the PYTHONHASHSEED to the same
    value.
    """
    random_hex = hex(random.getrandbits(100))

    # strip the leading 0x and trailing L
    return random_hex[2:-1]

@pytest.fixture
def deposit():
    """ Raiden chain default deposit. """
    # Arbitrary initial balance for each channel, using a small number for
    # easier calculations during testing
    return 200

@pytest.fixture
def both_participants_deposit():
    """ Boolean flag indicating if both participants of a channel put a deposit. """
    return True

@pytest.fixture
def number_of_tokens():
    """ Number of tokens pre-registered in the test Registry. """
    return 1

@pytest.fixture
def number_of_nodes():
    """ Number of raiden nodes in the test network. """
    return 3

@pytest.fixture
def channels_per_node():
    """ Number of pre-created channels per test raiden node. """
    return 1

@pytest.fixture
def poll_timeout():
    """ Timeout in seconds for polling a cluster. Used for geth. """
    return DEFAULT_POLL_TIMEOUT

@pytest.fixture
def transport_class():
    return UDPTransport

@pytest.fixture
def retry_interval():
    return 0.5

@pytest.fixture
def retries_before_backoff():
    return 2

@pytest.fixture
def throttle_capacity():
    return DEFAULT_PROTOCOL_THROTTLE_CAPACITY

@pytest.fixture
def throttle_fill_rate():
    return DEFAULT_PROTOCOL_THROTTLE_FILL_RATE

@pytest.fixture
def nat_invitation_timeout():
    return 5

@pytest.fixture
def nat_keepalive_retries():
    return 2

@pytest.fixture
def nat_keepalive_timeout():
    return 1

@pytest.fixture
def privatekey_seed(request):
    """ Private key template, allow different keys to be used for each test to
    avoid collisions.
    """
    return request.node.name + ':{}'

@pytest.fixture
def token_amount(number_of_nodes, deposit):
    total_per_node = 3 * (deposit + 1)
    total_token = total_per_node * number_of_nodes
    return total_token

@pytest.fixture
def network_wait():
    """Time in seconds used to wait for network events."""
    return 0.3

@pytest.fixture
def private_keys(number_of_nodes, privatekey_seed):
    """ Private keys for each raiden node. """

    # Note: The fixtures depend on the order of the private keys
    result = [
        sha3(privatekey_seed.format(position).encode())
        for position in range(number_of_nodes)
    ]

    # this must not happen, otherwise the keys and addresses will be equal!
    assert len(set(result)) == number_of_nodes, '`privatekey_seed` generate repeated keys'

    return result

@pytest.fixture
def deploy_key(privatekey_seed):
    return sha3(privatekey_seed.format('deploykey').encode())

@pytest.fixture
def blockchain_type(request):
    return request.config.option.blockchain_type

@pytest.fixture(autouse=True)
def blockchain_cache(request):
    """Expose flag --blockchain-cache value.

    This fixture allows tests to overwrite the blockchain_cache and force its
    value to True/False.
    """
    return request.config.option.blockchain_cache

@pytest.fixture
def blockchain_number_of_nodes():
    """ Number of nodes in the cluster, not the same as the number of raiden
    nodes. Used for all geth clusters and ignored for tester.
    """
    return 1

@pytest.fixture
def blockchain_key_seed():
    """ Private key template for the nodes in the private blockchain, allows
    different keys to be used for each test to avoid collisions.
    """
    return 'cluster:{}'

@pytest.fixture
def blockchain_private_keys(blockchain_number_of_nodes, blockchain_key_seed):
    """ The private keys for the each private chain node, not the same as the
    raiden's private key.
    """
    return [
        sha3(blockchain_key_seed.format(position).encode())
        for position in range(blockchain_number_of_nodes)
    ]

@pytest.fixture(scope='session')
def port_generator(request):
    """ count generator used to get a unique port number. """
    return get_free_port('127.0.0.1', request.config.option.initial_port)

@pytest.fixture
def blockchain_rpc_ports(blockchain_number_of_nodes, port_generator):
    """ A list of unique port numbers to be used by the blockchain nodes for
    the json-rpc interface.
    """
    return [
        next(port_generator)
        for _ in range(blockchain_number_of_nodes)
    ]

@pytest.fixture
def blockchain_p2p_ports(blockchain_number_of_nodes, port_generator):
    """ A list of unique port numbers to be used by the blockchain nodes for
    the p2p protocol.
    """
    return [
        next(port_generator)
        for _ in range(blockchain_number_of_nodes)
    ]

@pytest.fixture
def raiden_udp_ports(number_of_nodes, port_generator):
    """ A list of unique port numbers to be used by the raiden apps for the udp
    protocol.
    """
    return [
        next(port_generator)
        for _ in range(number_of_nodes)
    ]

@pytest.fixture
def rest_api_port_number(port_generator):
    """ Unique port for the REST API server. """
    return next(port_generator)

@pytest.fixture
def in_memory_database():
    """A boolean value indicating whether the sqlite3 databases will be in memory
    or in normal files. Defaults to True (in memory)."""
    return True

@pytest.fixture
def database_paths(tmpdir, private_keys, in_memory_database):
    """ Sqlite database paths for each app.
    """
    # According to http://www.sqlite.org/inmemorydb.html each memory connection will
    # create a unique in-memory DB, which is exactly what we need in this case for
    # each different Raiden app
    if in_memory_database:
        return [
            ':memory:'
            for position in range(len(private_keys))
        ]

    database_paths = list()
    for idx, pkey in enumerate(private_keys):
        app_dir = os.path.join(tmpdir.strpath, address_encoder(privatekey_to_address(pkey))[2:8])
        if not os.path.exists(app_dir):
            os.makedirs(app_dir)
        database_paths.append(os.path.join(app_dir, 'log.db'))

    return database_paths
<|endoftext|>