├── .editorconfig
├── .gitattributes
├── .gitignore
├── .jshintrc
├── .travis.yml
├── LICENSE
├── README.md
├── app
├── index.js
└── templates
│ ├── Gruntfile.js
│ ├── README.md.erb
│ ├── _bower.json
│ ├── _generator.json
│ ├── _package.json
│ ├── app
│ ├── ___init__.py
│ ├── models
│ │ └── ___init__.py
│ ├── routes
│ │ ├── ___init__.py
│ │ └── _index.py
│ └── static
│ │ ├── _index.html
│ │ ├── css
│ │ └── app.css
│ │ ├── js
│ │ ├── _app.js
│ │ └── home
│ │ │ └── _home-controller.js
│ │ └── views
│ │ └── home
│ │ └── _home.html
│ ├── bowerrc
│ ├── config.py
│ ├── db_create.py
│ ├── db_downgrade.py
│ ├── db_migrate.py
│ ├── db_upgrade.py
│ ├── editorconfig
│ ├── gitignore
│ ├── install.bat
│ ├── install.sh
│ ├── jshintrc
│ ├── run.py
│ └── virtualenv.py
├── entity
├── index.js
└── templates
│ ├── _generator.json
│ └── app
│ ├── models
│ └── _entity.py
│ ├── routes
│ └── _entities.py
│ └── static
│ ├── js
│ └── entity
│ │ ├── _entity-controller.js
│ │ ├── _entity-router.js
│ │ └── _entity-service.js
│ └── views
│ └── entity
│ └── _entities.html
├── package.json
└── test
├── test-creation.js
└── test-load.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | temp/
3 | npm-debug.log
4 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "camelcase": true,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "indent": 4,
10 | "latedef": true,
11 | "newcap": true,
12 | "noarg": true,
13 | "quotmark": "single",
14 | "regexp": true,
15 | "undef": true,
16 | "unused": true,
17 | "strict": true,
18 | "trailing": true,
19 | "smarttabs": true,
20 | "white": true
21 | }
22 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | - '0.8'
5 | before_install:
6 | - currentfolder=${PWD##*/}
7 | - if [ "$currentfolder" != 'generator-angular-flask' ]; then cd .. && eval "mv $currentfolder generator-angular-flask" && cd generator-angular-flask; fi
8 |
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2013 Robert Yokota
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The Angular-Flask generator
2 |
3 | A [Yeoman](http://yeoman.io) generator for [AngularJS](http://angularjs.org) and [Flask](http://flask.pocoo.org).
4 |
5 | Flask is a Python-based micro-framework. For AngularJS integration with other micro-frameworks, see https://github.com/rayokota/MicroFrameworkRosettaStone.
6 |
7 | ## Installation
8 |
9 | Install [Git](http://git-scm.com), [node.js](http://nodejs.org), and [Python 2.7](http://www.python.org/). The development mode also requires [SQLite](http://www.sqlite.org).
10 |
11 | Install Yeoman:
12 |
13 | npm install -g yo
14 |
15 | Install the Angular-Flask generator:
16 |
17 | npm install -g generator-angular-flask
18 |
19 | The above prerequisites can be installed to a VM using the [Angular-Flask provisioner](https://github.com/rayokota/provision-angular-flask).
20 |
21 | ## Creating a Flask service
22 |
23 | In a new directory, generate the service:
24 |
25 | yo angular-flask
26 |
27 | Install a virtual environment in new `flask` directory using `install.sh` (or `install.bat` for Windows):
28 |
29 | ./install.sh
30 |
31 | Run the service:
32 |
33 | flask/bin/python run.py
34 |
35 | Your service will run at [http://localhost:5000](http://localhost:5000).
36 |
37 |
38 | ## Creating a persistent entity
39 |
40 | Generate the entity:
41 |
42 | yo angular-flask:entity [myentity]
43 |
44 | You will be asked to specify attributes for the entity, where each attribute has the following:
45 |
46 | - a name
47 | - a type (String, Integer, Float, Boolean, Date, Enum)
48 | - for a String attribute, an optional minimum and maximum length
49 | - for a numeric attribute, an optional minimum and maximum value
50 | - for a Date attribute, an optional constraint to either past values or future values
51 | - for an Enum attribute, a list of enumerated values
52 | - whether the attribute is required
53 |
54 | Files that are regenerated will appear as conflicts. Allow the generator to overwrite these files as long as no custom changes have been made.
55 |
56 | Create the database as described in [this blog](http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iv-database).
57 |
58 | flask/bin/python db_create.py
59 |
60 | Run the service:
61 |
62 | flask/bin/python run.py
63 |
64 | A client-side AngularJS application will now be available by running
65 |
66 | grunt server
67 |
68 | The Grunt server will run at [http://localhost:9000](http://localhost:9000). It will proxy REST requests to the Flask service running at [http://localhost:5000](http://localhost:5000).
69 |
70 | At this point you should be able to navigate to a page to manage your persistent entities.
71 |
72 | The Grunt server supports hot reloading of client-side HTML/CSS/Javascript file changes.
73 |
74 |
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var util = require('util'),
3 | path = require('path'),
4 | yeoman = require('yeoman-generator'),
5 | _ = require('lodash'),
6 | _s = require('underscore.string'),
7 | pluralize = require('pluralize'),
8 | asciify = require('asciify');
9 |
10 | var AngularFlaskGenerator = module.exports = function AngularFlaskGenerator(args, options, config) {
11 | yeoman.generators.Base.apply(this, arguments);
12 |
13 | this.on('end', function () {
14 | this.installDependencies({ skipInstall: options['skip-install'] });
15 | });
16 |
17 | this.pkg = JSON.parse(this.readFileAsString(path.join(__dirname, '../package.json')));
18 | };
19 |
20 | util.inherits(AngularFlaskGenerator, yeoman.generators.Base);
21 |
22 | AngularFlaskGenerator.prototype.askFor = function askFor() {
23 |
24 | var cb = this.async();
25 |
26 | console.log('\n' +
27 | '+-+-+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+\n' +
28 | '|a|n|g|u|l|a|r| |f|l|a|s|k| |g|e|n|e|r|a|t|o|r|\n' +
29 | '+-+-+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+\n' +
30 | '\n');
31 |
32 | var prompts = [{
33 | type: 'input',
34 | name: 'baseName',
35 | message: 'What is the name of your application?',
36 | default: 'myapp'
37 | }];
38 |
39 | this.prompt(prompts, function (props) {
40 | this.baseName = props.baseName;
41 |
42 | cb();
43 | }.bind(this));
44 | };
45 |
46 | AngularFlaskGenerator.prototype.app = function app() {
47 |
48 | this.entities = [];
49 | this.resources = [];
50 | this.generatorConfig = {
51 | "baseName": this.baseName,
52 | "entities": this.entities,
53 | "resources": this.resources
54 | };
55 | this.generatorConfigStr = JSON.stringify(this.generatorConfig, null, '\t');
56 |
57 | this.template('_generator.json', 'generator.json');
58 | this.template('_package.json', 'package.json');
59 | this.template('_bower.json', 'bower.json');
60 | this.template('bowerrc', '.bowerrc');
61 | this.template('Gruntfile.js', 'Gruntfile.js');
62 | this.copy('gitignore', '.gitignore');
63 |
64 | var appDir = 'app/'
65 | var modelsDir = appDir + 'models/'
66 | var routesDir = appDir + 'routes/'
67 | var staticDir = appDir + 'static/'
68 | var templatesDir = appDir + 'templates/'
69 | this.mkdir(appDir);
70 | this.mkdir(modelsDir);
71 | this.mkdir(routesDir);
72 | this.mkdir(staticDir);
73 | this.mkdir(templatesDir);
74 |
75 | this.copy('install.bat', 'install.bat');
76 | this.copy('install.sh', 'install.sh');
77 | this.copy('config.py', 'config.py');
78 | this.copy('db_create.py', 'db_create.py');
79 | this.copy('db_downgrade.py', 'db_downgrade.py');
80 | this.copy('db_migrate.py', 'db_migrate.py');
81 | this.copy('db_upgrade.py', 'db_upgrade.py');
82 | this.copy('run.py', 'run.py');
83 | this.copy('virtualenv.py', 'virtualenv.py');
84 | this.template('app/___init__.py', appDir + '__init__.py');
85 | this.template('app/models/___init__.py', modelsDir + '__init__.py');
86 | this.template('app/routes/___init__.py', routesDir + '__init__.py');
87 | this.template('app/routes/_index.py', routesDir + 'index.py');
88 |
89 | var staticCssDir = staticDir + 'css/';
90 | var staticJsDir = staticDir + 'js/';
91 | var staticViewDir = staticDir + 'views/';
92 | this.mkdir(staticCssDir);
93 | this.mkdir(staticJsDir);
94 | this.mkdir(staticViewDir);
95 | this.template('app/static/_index.html', staticDir + 'index.html');
96 | this.copy('app/static/css/app.css', staticCssDir + 'app.css');
97 | this.template('app/static/js/_app.js', staticJsDir + 'app.js');
98 | this.template('app/static/js/home/_home-controller.js', staticJsDir + 'home/home-controller.js');
99 | this.template('app/static/views/home/_home.html', staticViewDir + 'home/home.html');
100 | };
101 |
102 | AngularFlaskGenerator.prototype.projectfiles = function projectfiles() {
103 | this.copy('editorconfig', '.editorconfig');
104 | this.copy('jshintrc', '.jshintrc');
105 | };
106 |
--------------------------------------------------------------------------------
/app/templates/Gruntfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest;
4 |
5 | module.exports = function (grunt) {
6 | require('load-grunt-tasks')(grunt);
7 | require('time-grunt')(grunt);
8 |
9 | grunt.initConfig({
10 | yeoman: {
11 | // configurable paths
12 | app: require('./bower.json').appPath || 'app/static',
13 | dist: 'app/static'
14 | },
15 | sync: {
16 | dist: {
17 | files: [{
18 | cwd: '<%%= yeoman.app %>',
19 | dest: '<%%= yeoman.dist %>',
20 | src: '**'
21 | }]
22 | }
23 | },
24 | watch: {
25 | options: {
26 | livereload: 35729
27 | },
28 | src: {
29 | files: [
30 | '<%%= yeoman.app %>/*.html',
31 | '<%%= yeoman.app %>/css/**/*',
32 | '<%%= yeoman.app %>/js/**/*',
33 | '<%%= yeoman.app %>/views/**/*'
34 | ],
35 | //tasks: ['sync:dist']
36 | }
37 | },
38 | connect: {
39 | proxies: [
40 | {
41 | context: '/<%= baseName %>',
42 | host: 'localhost',
43 | port: 5000,
44 | https: false,
45 | changeOrigin: false
46 | }
47 | ],
48 | options: {
49 | port: 9000,
50 | // Change this to '0.0.0.0' to access the server from outside.
51 | hostname: 'localhost',
52 | livereload: 35729
53 | },
54 | livereload: {
55 | options: {
56 | open: true,
57 | base: [
58 | '<%%= yeoman.app %>'
59 | ],
60 | middleware: function (connect) {
61 | return [
62 | proxySnippet,
63 | connect.static(require('path').resolve('app/static'))
64 | ];
65 | }
66 | }
67 | },
68 | /*
69 | dist: {
70 | options: {
71 | base: '<%%= yeoman.dist %>'
72 | }
73 | }
74 | */
75 | },
76 | // Put files not handled in other tasks here
77 | copy: {
78 | dist: {
79 | files: [{
80 | expand: true,
81 | dot: true,
82 | cwd: '<%%= yeoman.app %>',
83 | dest: '<%%= yeoman.dist %>',
84 | src: '**'
85 | }]
86 | },
87 | },
88 | // Test settings
89 | karma: {
90 | unit: {
91 | configFile: 'test/config/karma.conf.js',
92 | singleRun: true
93 | }
94 | },
95 | bowercopy: {
96 | options: {
97 | destPrefix: '<%%= yeoman.app %>'
98 | },
99 | test: {
100 | files: {
101 | 'test/lib/angular-mocks': 'angular-mocks',
102 | 'test/lib/angular-scenario': 'angular-scenario'
103 | }
104 | }
105 | }
106 | });
107 |
108 | grunt.registerTask('server', function (target) {
109 | grunt.task.run([
110 | //'copy:dist',
111 | 'configureProxies',
112 | 'connect:livereload',
113 | 'watch'
114 | ]);
115 | });
116 | };
117 |
--------------------------------------------------------------------------------
/app/templates/README.md.erb:
--------------------------------------------------------------------------------
1 | ## Overview
2 |
3 | Blah blah
4 |
5 |
--------------------------------------------------------------------------------
/app/templates/_bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= _.camelize(baseName) %>",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular-bootstrap": "~0.10.0",
6 | "angular-resource": "~1.2.13",
7 | "angular-route": "~1.2.13",
8 | "angular-ui-date": "~0.0.3",
9 | "angular": "~1.2.13",
10 | "bootstrap-css": "~3.0.0",
11 | "jquery": "~2.1.0",
12 | "lodash": "~2.4.1"
13 | },
14 | "devDependencies": {
15 | "angular-mocks": "~1.2.13",
16 | "angular-scenario": "~1.2.13"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/templates/_generator.json:
--------------------------------------------------------------------------------
1 | <%= generatorConfigStr %>
2 |
--------------------------------------------------------------------------------
/app/templates/_package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= _.slugify(baseName) %>",
3 | "version": "0.0.0",
4 | "description": "Description for <%= baseName %>",
5 | "dependencies": {},
6 | "devDependencies": {
7 | "grunt": "~0.4.2",
8 | "grunt-autoprefixer": "~0.4.2",
9 | "grunt-bowercopy": "~0.4.1",
10 | "grunt-bower-install": "~0.7.0",
11 | "grunt-concurrent": "~0.4.2",
12 | "grunt-connect-proxy": "~0.2.0",
13 | "grunt-contrib-clean": "~0.5.0",
14 | "grunt-contrib-concat": "~0.3.0",
15 | "grunt-contrib-connect": "~0.5.0",
16 | "grunt-contrib-copy": "~0.4.1",
17 | "grunt-contrib-cssmin": "~0.7.0",
18 | "grunt-contrib-htmlmin": "~0.1.3",
19 | "grunt-contrib-imagemin": "~0.4.0",
20 | "grunt-contrib-jshint": "~0.7.2",
21 | "grunt-contrib-uglify": "~0.2.7",
22 | "grunt-contrib-watch": "~0.5.3",
23 | "grunt-karma": "~0.6.2",
24 | "grunt-modernizr": "~0.4.1",
25 | "grunt-ngmin": "~0.0.3",
26 | "grunt-rev": "~0.1.0",
27 | "grunt-svgmin": "~0.3.0",
28 | "grunt-sync": "~0.0.5",
29 | "grunt-usemin": "~2.0.2",
30 | "load-grunt-tasks": "~0.2.0",
31 | "time-grunt": "0.2.3",
32 | "karma" : "~0.10.8",
33 | "karma-junit-reporter" : "~0.1.0",
34 | "karma-jasmine" : "~0.1.0",
35 | "karma-ng-scenario" : "~0.1.0"
36 | },
37 | "engines": {
38 | "node": ">=0.8.15"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/templates/app/___init__.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 | from flask.ext.sqlalchemy import SQLAlchemy
3 |
4 | app = Flask(__name__, static_url_path='')
5 | app.config.from_object('config')
6 | db = SQLAlchemy(app)
7 |
8 | <% _.each(entities, function (entity) { %>
9 | from app.models import <%= entity.name %><% }); %>
10 | from app.routes import index
11 | <% _.each(entities, function (entity) { %>
12 | from app.routes import <%= pluralize(entity.name) %><% }); %>
13 |
--------------------------------------------------------------------------------
/app/templates/app/models/___init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rayokota/generator-angular-flask/53f2a5ef59c644dfeda9be7c2fe016ba0deefd26/app/templates/app/models/___init__.py
--------------------------------------------------------------------------------
/app/templates/app/routes/___init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rayokota/generator-angular-flask/53f2a5ef59c644dfeda9be7c2fe016ba0deefd26/app/templates/app/routes/___init__.py
--------------------------------------------------------------------------------
/app/templates/app/routes/_index.py:
--------------------------------------------------------------------------------
1 | from app import app
2 |
3 | @app.route('/')
4 | def root():
5 | return app.send_static_file('index.html')
6 |
--------------------------------------------------------------------------------
/app/templates/app/static/_index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= _.capitalize(baseName) %>
8 |
9 |
10 |
11 |
12 |
13 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | <% _.each(entities, function (entity) { %>
52 |
53 |
54 |
55 | <% }); %>
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/templates/app/static/css/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin-top: 60px;
3 | padding: 10px;
4 | background-color: #ECF0F1;
5 | }
6 |
--------------------------------------------------------------------------------
/app/templates/app/static/js/_app.js:
--------------------------------------------------------------------------------
1 | // Declare app level module which depends on filters, and services
2 | angular.module('<%= baseName %>', ['ngResource', 'ngRoute', 'ui.bootstrap', 'ui.date'])
3 | .config(['$routeProvider', function ($routeProvider) {
4 | $routeProvider
5 | .when('/', {
6 | templateUrl: 'views/home/home.html',
7 | controller: 'HomeController'})
8 | .otherwise({redirectTo: '/'});
9 | }]);
10 |
--------------------------------------------------------------------------------
/app/templates/app/static/js/home/_home-controller.js:
--------------------------------------------------------------------------------
1 | angular.module('<%= baseName %>')
2 | .controller('HomeController', ['$scope', function ($scope) {
3 | }]);
4 |
--------------------------------------------------------------------------------
/app/templates/app/static/views/home/_home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Welcome to <%= _.capitalize(baseName) %>!
5 |
This is your homepage, ready for editing
6 |
7 |
8 | If you like this Angular-Flask generator, please give us a star at Github!
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/templates/bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "app/static/lib",
3 | "json": "bower.json"
4 | }
5 |
--------------------------------------------------------------------------------
/app/templates/config.py:
--------------------------------------------------------------------------------
1 | import os
2 | basedir = os.path.abspath(os.path.dirname(__file__))
3 |
4 | SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
5 | SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
6 |
--------------------------------------------------------------------------------
/app/templates/db_create.py:
--------------------------------------------------------------------------------
1 | #!flask/bin/python
2 | from migrate.versioning import api
3 | from config import SQLALCHEMY_DATABASE_URI
4 | from config import SQLALCHEMY_MIGRATE_REPO
5 | from app import db
6 | import os.path
7 | db.create_all()
8 | if not os.path.exists(SQLALCHEMY_MIGRATE_REPO):
9 | api.create(SQLALCHEMY_MIGRATE_REPO, 'database repository')
10 | api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
11 | else:
12 | api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, api.version(SQLALCHEMY_MIGRATE_REPO))
13 |
--------------------------------------------------------------------------------
/app/templates/db_downgrade.py:
--------------------------------------------------------------------------------
1 | #!flask/bin/python
2 | from migrate.versioning import api
3 | from config import SQLALCHEMY_DATABASE_URI
4 | from config import SQLALCHEMY_MIGRATE_REPO
5 | v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
6 | api.downgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, v - 1)
7 | print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))
8 |
--------------------------------------------------------------------------------
/app/templates/db_migrate.py:
--------------------------------------------------------------------------------
1 | #!flask/bin/python
2 | import imp
3 | from migrate.versioning import api
4 | from app import db
5 | from config import SQLALCHEMY_DATABASE_URI
6 | from config import SQLALCHEMY_MIGRATE_REPO
7 | migration = SQLALCHEMY_MIGRATE_REPO + '/versions/%03d_migration.py' % (api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) + 1)
8 | tmp_module = imp.new_module('old_model')
9 | old_model = api.create_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
10 | exec old_model in tmp_module.__dict__
11 | script = api.make_update_script_for_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, tmp_module.meta, db.metadata)
12 | open(migration, "wt").write(script)
13 | api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
14 | print 'New migration saved as ' + migration
15 | print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))
16 |
--------------------------------------------------------------------------------
/app/templates/db_upgrade.py:
--------------------------------------------------------------------------------
1 | #!flask/bin/python
2 | from migrate.versioning import api
3 | from config import SQLALCHEMY_DATABASE_URI
4 | from config import SQLALCHEMY_MIGRATE_REPO
5 | api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
6 | print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))
7 |
--------------------------------------------------------------------------------
/app/templates/editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/app/templates/gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.db
3 | *.pyc
4 | *.swp
5 | *~
6 | db_repository
7 | flask
8 | node_modules
9 |
--------------------------------------------------------------------------------
/app/templates/install.bat:
--------------------------------------------------------------------------------
1 | python virtualenv.py flask
2 | flask\Scripts\pip install setuptools --no-use-wheel --upgrade
3 | flask\Scripts\pip install flask==0.9
4 | flask\Scripts\pip install flask-login
5 | flask\Scripts\pip install flask-openid
6 | flask\Scripts\pip install sqlalchemy==0.7.9
7 | flask\Scripts\pip install flask-sqlalchemy==0.16
8 | flask\Scripts\pip install sqlalchemy-migrate==0.7.2
9 | flask\Scripts\pip install flask-whooshalchemy==0.54a
10 | flask\Scripts\pip install flask-wtf==0.8.4
11 | flask\Scripts\pip install pytz==2013b
12 | flask\Scripts\pip install flask-babel==0.8
13 | flask\Scripts\pip install flup
14 |
--------------------------------------------------------------------------------
/app/templates/install.sh:
--------------------------------------------------------------------------------
1 | python virtualenv.py flask
2 | flask/bin/pip install setuptools --no-use-wheel --upgrade
3 | flask/bin/pip install flask==0.9
4 | flask/bin/pip install flask-login
5 | flask/bin/pip install flask-openid
6 | flask/bin/pip install flask-mail==0.7.6
7 | flask/bin/pip install sqlalchemy==0.7.9
8 | flask/bin/pip install flask-sqlalchemy==0.16
9 | flask/bin/pip install sqlalchemy-migrate==0.7.2
10 | flask/bin/pip install flask-whooshalchemy==0.54a
11 | flask/bin/pip install flask-wtf==0.8.4
12 | flask/bin/pip install pytz==2013b
13 | flask/bin/pip install flask-babel==0.8
14 | flask/bin/pip install flup
15 |
--------------------------------------------------------------------------------
/app/templates/jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "camelcase": true,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "indent": 4,
10 | "latedef": true,
11 | "newcap": true,
12 | "noarg": true,
13 | "quotmark": "single",
14 | "regexp": true,
15 | "undef": true,
16 | "unused": true,
17 | "strict": true,
18 | "trailing": true,
19 | "smarttabs": true,
20 | "white": true
21 | }
22 |
--------------------------------------------------------------------------------
/app/templates/run.py:
--------------------------------------------------------------------------------
1 | from app import app
2 | app.run(debug = True)
3 |
--------------------------------------------------------------------------------
/app/templates/virtualenv.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Create a "virtual" Python installation
3 | """
4 |
5 | # If you change the version here, change it in setup.py
6 | # and docs/conf.py as well.
7 | __version__ = "1.9.1" # following best practices
8 | virtualenv_version = __version__ # legacy, again
9 |
10 | import base64
11 | import sys
12 | import os
13 | import codecs
14 | import optparse
15 | import re
16 | import shutil
17 | import logging
18 | import tempfile
19 | import zlib
20 | import errno
21 | import glob
22 | import distutils.sysconfig
23 | from distutils.util import strtobool
24 | import struct
25 | import subprocess
26 |
27 | if sys.version_info < (2, 5):
28 | print('ERROR: %s' % sys.exc_info()[1])
29 | print('ERROR: this script requires Python 2.5 or greater.')
30 | sys.exit(101)
31 |
32 | try:
33 | set
34 | except NameError:
35 | from sets import Set as set
36 | try:
37 | basestring
38 | except NameError:
39 | basestring = str
40 |
41 | try:
42 | import ConfigParser
43 | except ImportError:
44 | import configparser as ConfigParser
45 |
46 | join = os.path.join
47 | py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1])
48 |
49 | is_jython = sys.platform.startswith('java')
50 | is_pypy = hasattr(sys, 'pypy_version_info')
51 | is_win = (sys.platform == 'win32')
52 | is_cygwin = (sys.platform == 'cygwin')
53 | is_darwin = (sys.platform == 'darwin')
54 | abiflags = getattr(sys, 'abiflags', '')
55 |
56 | user_dir = os.path.expanduser('~')
57 | if is_win:
58 | default_storage_dir = os.path.join(user_dir, 'virtualenv')
59 | else:
60 | default_storage_dir = os.path.join(user_dir, '.virtualenv')
61 | default_config_file = os.path.join(default_storage_dir, 'virtualenv.ini')
62 |
63 | if is_pypy:
64 | expected_exe = 'pypy'
65 | elif is_jython:
66 | expected_exe = 'jython'
67 | else:
68 | expected_exe = 'python'
69 |
70 |
71 | REQUIRED_MODULES = ['os', 'posix', 'posixpath', 'nt', 'ntpath', 'genericpath',
72 | 'fnmatch', 'locale', 'encodings', 'codecs',
73 | 'stat', 'UserDict', 'readline', 'copy_reg', 'types',
74 | 're', 'sre', 'sre_parse', 'sre_constants', 'sre_compile',
75 | 'zlib']
76 |
77 | REQUIRED_FILES = ['lib-dynload', 'config']
78 |
79 | majver, minver = sys.version_info[:2]
80 | if majver == 2:
81 | if minver >= 6:
82 | REQUIRED_MODULES.extend(['warnings', 'linecache', '_abcoll', 'abc'])
83 | if minver >= 7:
84 | REQUIRED_MODULES.extend(['_weakrefset'])
85 | if minver <= 3:
86 | REQUIRED_MODULES.extend(['sets', '__future__'])
87 | elif majver == 3:
88 | # Some extra modules are needed for Python 3, but different ones
89 | # for different versions.
90 | REQUIRED_MODULES.extend(['_abcoll', 'warnings', 'linecache', 'abc', 'io',
91 | '_weakrefset', 'copyreg', 'tempfile', 'random',
92 | '__future__', 'collections', 'keyword', 'tarfile',
93 | 'shutil', 'struct', 'copy', 'tokenize', 'token',
94 | 'functools', 'heapq', 'bisect', 'weakref',
95 | 'reprlib'])
96 | if minver >= 2:
97 | REQUIRED_FILES[-1] = 'config-%s' % majver
98 | if minver == 3:
99 | import sysconfig
100 | platdir = sysconfig.get_config_var('PLATDIR')
101 | REQUIRED_FILES.append(platdir)
102 | # The whole list of 3.3 modules is reproduced below - the current
103 | # uncommented ones are required for 3.3 as of now, but more may be
104 | # added as 3.3 development continues.
105 | REQUIRED_MODULES.extend([
106 | #"aifc",
107 | #"antigravity",
108 | #"argparse",
109 | #"ast",
110 | #"asynchat",
111 | #"asyncore",
112 | "base64",
113 | #"bdb",
114 | #"binhex",
115 | #"bisect",
116 | #"calendar",
117 | #"cgi",
118 | #"cgitb",
119 | #"chunk",
120 | #"cmd",
121 | #"codeop",
122 | #"code",
123 | #"colorsys",
124 | #"_compat_pickle",
125 | #"compileall",
126 | #"concurrent",
127 | #"configparser",
128 | #"contextlib",
129 | #"cProfile",
130 | #"crypt",
131 | #"csv",
132 | #"ctypes",
133 | #"curses",
134 | #"datetime",
135 | #"dbm",
136 | #"decimal",
137 | #"difflib",
138 | #"dis",
139 | #"doctest",
140 | #"dummy_threading",
141 | "_dummy_thread",
142 | #"email",
143 | #"filecmp",
144 | #"fileinput",
145 | #"formatter",
146 | #"fractions",
147 | #"ftplib",
148 | #"functools",
149 | #"getopt",
150 | #"getpass",
151 | #"gettext",
152 | #"glob",
153 | #"gzip",
154 | "hashlib",
155 | #"heapq",
156 | "hmac",
157 | #"html",
158 | #"http",
159 | #"idlelib",
160 | #"imaplib",
161 | #"imghdr",
162 | "imp",
163 | "importlib",
164 | #"inspect",
165 | #"json",
166 | #"lib2to3",
167 | #"logging",
168 | #"macpath",
169 | #"macurl2path",
170 | #"mailbox",
171 | #"mailcap",
172 | #"_markupbase",
173 | #"mimetypes",
174 | #"modulefinder",
175 | #"multiprocessing",
176 | #"netrc",
177 | #"nntplib",
178 | #"nturl2path",
179 | #"numbers",
180 | #"opcode",
181 | #"optparse",
182 | #"os2emxpath",
183 | #"pdb",
184 | #"pickle",
185 | #"pickletools",
186 | #"pipes",
187 | #"pkgutil",
188 | #"platform",
189 | #"plat-linux2",
190 | #"plistlib",
191 | #"poplib",
192 | #"pprint",
193 | #"profile",
194 | #"pstats",
195 | #"pty",
196 | #"pyclbr",
197 | #"py_compile",
198 | #"pydoc_data",
199 | #"pydoc",
200 | #"_pyio",
201 | #"queue",
202 | #"quopri",
203 | #"reprlib",
204 | "rlcompleter",
205 | #"runpy",
206 | #"sched",
207 | #"shelve",
208 | #"shlex",
209 | #"smtpd",
210 | #"smtplib",
211 | #"sndhdr",
212 | #"socket",
213 | #"socketserver",
214 | #"sqlite3",
215 | #"ssl",
216 | #"stringprep",
217 | #"string",
218 | #"_strptime",
219 | #"subprocess",
220 | #"sunau",
221 | #"symbol",
222 | #"symtable",
223 | #"sysconfig",
224 | #"tabnanny",
225 | #"telnetlib",
226 | #"test",
227 | #"textwrap",
228 | #"this",
229 | #"_threading_local",
230 | #"threading",
231 | #"timeit",
232 | #"tkinter",
233 | #"tokenize",
234 | #"token",
235 | #"traceback",
236 | #"trace",
237 | #"tty",
238 | #"turtledemo",
239 | #"turtle",
240 | #"unittest",
241 | #"urllib",
242 | #"uuid",
243 | #"uu",
244 | #"wave",
245 | #"weakref",
246 | #"webbrowser",
247 | #"wsgiref",
248 | #"xdrlib",
249 | #"xml",
250 | #"xmlrpc",
251 | #"zipfile",
252 | ])
253 |
254 | if is_pypy:
255 | # these are needed to correctly display the exceptions that may happen
256 | # during the bootstrap
257 | REQUIRED_MODULES.extend(['traceback', 'linecache'])
258 |
259 | class Logger(object):
260 |
261 | """
262 | Logging object for use in command-line script. Allows ranges of
263 | levels, to avoid some redundancy of displayed information.
264 | """
265 |
266 | DEBUG = logging.DEBUG
267 | INFO = logging.INFO
268 | NOTIFY = (logging.INFO+logging.WARN)/2
269 | WARN = WARNING = logging.WARN
270 | ERROR = logging.ERROR
271 | FATAL = logging.FATAL
272 |
273 | LEVELS = [DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL]
274 |
275 | def __init__(self, consumers):
276 | self.consumers = consumers
277 | self.indent = 0
278 | self.in_progress = None
279 | self.in_progress_hanging = False
280 |
281 | def debug(self, msg, *args, **kw):
282 | self.log(self.DEBUG, msg, *args, **kw)
283 | def info(self, msg, *args, **kw):
284 | self.log(self.INFO, msg, *args, **kw)
285 | def notify(self, msg, *args, **kw):
286 | self.log(self.NOTIFY, msg, *args, **kw)
287 | def warn(self, msg, *args, **kw):
288 | self.log(self.WARN, msg, *args, **kw)
289 | def error(self, msg, *args, **kw):
290 | self.log(self.ERROR, msg, *args, **kw)
291 | def fatal(self, msg, *args, **kw):
292 | self.log(self.FATAL, msg, *args, **kw)
293 | def log(self, level, msg, *args, **kw):
294 | if args:
295 | if kw:
296 | raise TypeError(
297 | "You may give positional or keyword arguments, not both")
298 | args = args or kw
299 | rendered = None
300 | for consumer_level, consumer in self.consumers:
301 | if self.level_matches(level, consumer_level):
302 | if (self.in_progress_hanging
303 | and consumer in (sys.stdout, sys.stderr)):
304 | self.in_progress_hanging = False
305 | sys.stdout.write('\n')
306 | sys.stdout.flush()
307 | if rendered is None:
308 | if args:
309 | rendered = msg % args
310 | else:
311 | rendered = msg
312 | rendered = ' '*self.indent + rendered
313 | if hasattr(consumer, 'write'):
314 | consumer.write(rendered+'\n')
315 | else:
316 | consumer(rendered)
317 |
318 | def start_progress(self, msg):
319 | assert not self.in_progress, (
320 | "Tried to start_progress(%r) while in_progress %r"
321 | % (msg, self.in_progress))
322 | if self.level_matches(self.NOTIFY, self._stdout_level()):
323 | sys.stdout.write(msg)
324 | sys.stdout.flush()
325 | self.in_progress_hanging = True
326 | else:
327 | self.in_progress_hanging = False
328 | self.in_progress = msg
329 |
330 | def end_progress(self, msg='done.'):
331 | assert self.in_progress, (
332 | "Tried to end_progress without start_progress")
333 | if self.stdout_level_matches(self.NOTIFY):
334 | if not self.in_progress_hanging:
335 | # Some message has been printed out since start_progress
336 | sys.stdout.write('...' + self.in_progress + msg + '\n')
337 | sys.stdout.flush()
338 | else:
339 | sys.stdout.write(msg + '\n')
340 | sys.stdout.flush()
341 | self.in_progress = None
342 | self.in_progress_hanging = False
343 |
344 | def show_progress(self):
345 | """If we are in a progress scope, and no log messages have been
346 | shown, write out another '.'"""
347 | if self.in_progress_hanging:
348 | sys.stdout.write('.')
349 | sys.stdout.flush()
350 |
351 | def stdout_level_matches(self, level):
352 | """Returns true if a message at this level will go to stdout"""
353 | return self.level_matches(level, self._stdout_level())
354 |
355 | def _stdout_level(self):
356 | """Returns the level that stdout runs at"""
357 | for level, consumer in self.consumers:
358 | if consumer is sys.stdout:
359 | return level
360 | return self.FATAL
361 |
362 | def level_matches(self, level, consumer_level):
363 | """
364 | >>> l = Logger([])
365 | >>> l.level_matches(3, 4)
366 | False
367 | >>> l.level_matches(3, 2)
368 | True
369 | >>> l.level_matches(slice(None, 3), 3)
370 | False
371 | >>> l.level_matches(slice(None, 3), 2)
372 | True
373 | >>> l.level_matches(slice(1, 3), 1)
374 | True
375 | >>> l.level_matches(slice(2, 3), 1)
376 | False
377 | """
378 | if isinstance(level, slice):
379 | start, stop = level.start, level.stop
380 | if start is not None and start > consumer_level:
381 | return False
382 | if stop is not None and stop <= consumer_level:
383 | return False
384 | return True
385 | else:
386 | return level >= consumer_level
387 |
388 | #@classmethod
389 | def level_for_integer(cls, level):
390 | levels = cls.LEVELS
391 | if level < 0:
392 | return levels[0]
393 | if level >= len(levels):
394 | return levels[-1]
395 | return levels[level]
396 |
397 | level_for_integer = classmethod(level_for_integer)
398 |
399 | # create a silent logger just to prevent this from being undefined
400 | # will be overridden with requested verbosity main() is called.
401 | logger = Logger([(Logger.LEVELS[-1], sys.stdout)])
402 |
403 | def mkdir(path):
404 | if not os.path.exists(path):
405 | logger.info('Creating %s', path)
406 | os.makedirs(path)
407 | else:
408 | logger.info('Directory %s already exists', path)
409 |
410 | def copyfileordir(src, dest):
411 | if os.path.isdir(src):
412 | shutil.copytree(src, dest, True)
413 | else:
414 | shutil.copy2(src, dest)
415 |
416 | def copyfile(src, dest, symlink=True):
417 | if not os.path.exists(src):
418 | # Some bad symlink in the src
419 | logger.warn('Cannot find file %s (bad symlink)', src)
420 | return
421 | if os.path.exists(dest):
422 | logger.debug('File %s already exists', dest)
423 | return
424 | if not os.path.exists(os.path.dirname(dest)):
425 | logger.info('Creating parent directories for %s' % os.path.dirname(dest))
426 | os.makedirs(os.path.dirname(dest))
427 | if not os.path.islink(src):
428 | srcpath = os.path.abspath(src)
429 | else:
430 | srcpath = os.readlink(src)
431 | if symlink and hasattr(os, 'symlink') and not is_win:
432 | logger.info('Symlinking %s', dest)
433 | try:
434 | os.symlink(srcpath, dest)
435 | except (OSError, NotImplementedError):
436 | logger.info('Symlinking failed, copying to %s', dest)
437 | copyfileordir(src, dest)
438 | else:
439 | logger.info('Copying to %s', dest)
440 | copyfileordir(src, dest)
441 |
442 | def writefile(dest, content, overwrite=True):
443 | if not os.path.exists(dest):
444 | logger.info('Writing %s', dest)
445 | f = open(dest, 'wb')
446 | f.write(content.encode('utf-8'))
447 | f.close()
448 | return
449 | else:
450 | f = open(dest, 'rb')
451 | c = f.read()
452 | f.close()
453 | if c != content.encode("utf-8"):
454 | if not overwrite:
455 | logger.notify('File %s exists with different content; not overwriting', dest)
456 | return
457 | logger.notify('Overwriting %s with new content', dest)
458 | f = open(dest, 'wb')
459 | f.write(content.encode('utf-8'))
460 | f.close()
461 | else:
462 | logger.info('Content %s already in place', dest)
463 |
464 | def rmtree(dir):
465 | if os.path.exists(dir):
466 | logger.notify('Deleting tree %s', dir)
467 | shutil.rmtree(dir)
468 | else:
469 | logger.info('Do not need to delete %s; already gone', dir)
470 |
471 | def make_exe(fn):
472 | if hasattr(os, 'chmod'):
473 | oldmode = os.stat(fn).st_mode & 0xFFF # 0o7777
474 | newmode = (oldmode | 0x16D) & 0xFFF # 0o555, 0o7777
475 | os.chmod(fn, newmode)
476 | logger.info('Changed mode of %s to %s', fn, oct(newmode))
477 |
478 | def _find_file(filename, dirs):
479 | for dir in reversed(dirs):
480 | files = glob.glob(os.path.join(dir, filename))
481 | if files and os.path.isfile(files[0]):
482 | return True, files[0]
483 | return False, filename
484 |
485 | def _install_req(py_executable, unzip=False, distribute=False,
486 | search_dirs=None, never_download=False):
487 |
488 | if search_dirs is None:
489 | search_dirs = file_search_dirs()
490 |
491 | if not distribute:
492 | egg_path = 'setuptools-*-py%s.egg' % sys.version[:3]
493 | found, egg_path = _find_file(egg_path, search_dirs)
494 | project_name = 'setuptools'
495 | bootstrap_script = EZ_SETUP_PY
496 | tgz_path = None
497 | else:
498 | # Look for a distribute egg (these are not distributed by default,
499 | # but can be made available by the user)
500 | egg_path = 'distribute-*-py%s.egg' % sys.version[:3]
501 | found, egg_path = _find_file(egg_path, search_dirs)
502 | project_name = 'distribute'
503 | if found:
504 | tgz_path = None
505 | bootstrap_script = DISTRIBUTE_FROM_EGG_PY
506 | else:
507 | # Fall back to sdist
508 | # NB: egg_path is not None iff tgz_path is None
509 | # iff bootstrap_script is a generic setup script accepting
510 | # the standard arguments.
511 | egg_path = None
512 | tgz_path = 'distribute-*.tar.gz'
513 | found, tgz_path = _find_file(tgz_path, search_dirs)
514 | bootstrap_script = DISTRIBUTE_SETUP_PY
515 |
516 | if is_jython and os._name == 'nt':
517 | # Jython's .bat sys.executable can't handle a command line
518 | # argument with newlines
519 | fd, ez_setup = tempfile.mkstemp('.py')
520 | os.write(fd, bootstrap_script)
521 | os.close(fd)
522 | cmd = [py_executable, ez_setup]
523 | else:
524 | cmd = [py_executable, '-c', bootstrap_script]
525 | if unzip and egg_path:
526 | cmd.append('--always-unzip')
527 | env = {}
528 | remove_from_env = ['__PYVENV_LAUNCHER__']
529 | if logger.stdout_level_matches(logger.DEBUG) and egg_path:
530 | cmd.append('-v')
531 |
532 | old_chdir = os.getcwd()
533 | if egg_path is not None and os.path.exists(egg_path):
534 | logger.info('Using existing %s egg: %s' % (project_name, egg_path))
535 | cmd.append(egg_path)
536 | if os.environ.get('PYTHONPATH'):
537 | env['PYTHONPATH'] = egg_path + os.path.pathsep + os.environ['PYTHONPATH']
538 | else:
539 | env['PYTHONPATH'] = egg_path
540 | elif tgz_path is not None and os.path.exists(tgz_path):
541 | # Found a tgz source dist, let's chdir
542 | logger.info('Using existing %s egg: %s' % (project_name, tgz_path))
543 | os.chdir(os.path.dirname(tgz_path))
544 | # in this case, we want to be sure that PYTHONPATH is unset (not
545 | # just empty, really unset), else CPython tries to import the
546 | # site.py that it's in virtualenv_support
547 | remove_from_env.append('PYTHONPATH')
548 | elif never_download:
549 | logger.fatal("Can't find any local distributions of %s to install "
550 | "and --never-download is set. Either re-run virtualenv "
551 | "without the --never-download option, or place a %s "
552 | "distribution (%s) in one of these "
553 | "locations: %r" % (project_name, project_name,
554 | egg_path or tgz_path,
555 | search_dirs))
556 | sys.exit(1)
557 | elif egg_path:
558 | logger.info('No %s egg found; downloading' % project_name)
559 | cmd.extend(['--always-copy', '-U', project_name])
560 | else:
561 | logger.info('No %s tgz found; downloading' % project_name)
562 | logger.start_progress('Installing %s...' % project_name)
563 | logger.indent += 2
564 | cwd = None
565 | if project_name == 'distribute':
566 | env['DONT_PATCH_SETUPTOOLS'] = 'true'
567 |
568 | def _filter_ez_setup(line):
569 | return filter_ez_setup(line, project_name)
570 |
571 | if not os.access(os.getcwd(), os.W_OK):
572 | cwd = tempfile.mkdtemp()
573 | if tgz_path is not None and os.path.exists(tgz_path):
574 | # the current working dir is hostile, let's copy the
575 | # tarball to a temp dir
576 | target = os.path.join(cwd, os.path.split(tgz_path)[-1])
577 | shutil.copy(tgz_path, target)
578 | try:
579 | call_subprocess(cmd, show_stdout=False,
580 | filter_stdout=_filter_ez_setup,
581 | extra_env=env,
582 | remove_from_env=remove_from_env,
583 | cwd=cwd)
584 | finally:
585 | logger.indent -= 2
586 | logger.end_progress()
587 | if cwd is not None:
588 | shutil.rmtree(cwd)
589 | if os.getcwd() != old_chdir:
590 | os.chdir(old_chdir)
591 | if is_jython and os._name == 'nt':
592 | os.remove(ez_setup)
593 |
594 | def file_search_dirs():
595 | here = os.path.dirname(os.path.abspath(__file__))
596 | dirs = ['.', here,
597 | join(here, 'virtualenv_support')]
598 | if os.path.splitext(os.path.dirname(__file__))[0] != 'virtualenv':
599 | # Probably some boot script; just in case virtualenv is installed...
600 | try:
601 | import virtualenv
602 | except ImportError:
603 | pass
604 | else:
605 | dirs.append(os.path.join(os.path.dirname(virtualenv.__file__), 'virtualenv_support'))
606 | return [d for d in dirs if os.path.isdir(d)]
607 |
608 | def install_setuptools(py_executable, unzip=False,
609 | search_dirs=None, never_download=False):
610 | _install_req(py_executable, unzip,
611 | search_dirs=search_dirs, never_download=never_download)
612 |
613 | def install_distribute(py_executable, unzip=False,
614 | search_dirs=None, never_download=False):
615 | _install_req(py_executable, unzip, distribute=True,
616 | search_dirs=search_dirs, never_download=never_download)
617 |
618 | _pip_re = re.compile(r'^pip-.*(zip|tar.gz|tar.bz2|tgz|tbz)$', re.I)
619 | def install_pip(py_executable, search_dirs=None, never_download=False):
620 | if search_dirs is None:
621 | search_dirs = file_search_dirs()
622 |
623 | filenames = []
624 | for dir in search_dirs:
625 | filenames.extend([join(dir, fn) for fn in os.listdir(dir)
626 | if _pip_re.search(fn)])
627 | filenames = [(os.path.basename(filename).lower(), i, filename) for i, filename in enumerate(filenames)]
628 | filenames.sort()
629 | filenames = [filename for basename, i, filename in filenames]
630 | if not filenames:
631 | filename = 'pip'
632 | else:
633 | filename = filenames[-1]
634 | easy_install_script = 'easy_install'
635 | if is_win:
636 | easy_install_script = 'easy_install-script.py'
637 | # There's two subtle issues here when invoking easy_install.
638 | # 1. On unix-like systems the easy_install script can *only* be executed
639 | # directly if its full filesystem path is no longer than 78 characters.
640 | # 2. A work around to [1] is to use the `python path/to/easy_install foo`
641 | # pattern, but that breaks if the path contains non-ASCII characters, as
642 | # you can't put the file encoding declaration before the shebang line.
643 | # The solution is to use Python's -x flag to skip the first line of the
644 | # script (and any ASCII decoding errors that may have occurred in that line)
645 | cmd = [py_executable, '-x', join(os.path.dirname(py_executable), easy_install_script), filename]
646 | # jython and pypy don't yet support -x
647 | if is_jython or is_pypy:
648 | cmd.remove('-x')
649 | if filename == 'pip':
650 | if never_download:
651 | logger.fatal("Can't find any local distributions of pip to install "
652 | "and --never-download is set. Either re-run virtualenv "
653 | "without the --never-download option, or place a pip "
654 | "source distribution (zip/tar.gz/tar.bz2) in one of these "
655 | "locations: %r" % search_dirs)
656 | sys.exit(1)
657 | logger.info('Installing pip from network...')
658 | else:
659 | logger.info('Installing existing %s distribution: %s' % (
660 | os.path.basename(filename), filename))
661 | logger.start_progress('Installing pip...')
662 | logger.indent += 2
663 | def _filter_setup(line):
664 | return filter_ez_setup(line, 'pip')
665 | try:
666 | call_subprocess(cmd, show_stdout=False,
667 | filter_stdout=_filter_setup)
668 | finally:
669 | logger.indent -= 2
670 | logger.end_progress()
671 |
672 | def filter_ez_setup(line, project_name='setuptools'):
673 | if not line.strip():
674 | return Logger.DEBUG
675 | if project_name == 'distribute':
676 | for prefix in ('Extracting', 'Now working', 'Installing', 'Before',
677 | 'Scanning', 'Setuptools', 'Egg', 'Already',
678 | 'running', 'writing', 'reading', 'installing',
679 | 'creating', 'copying', 'byte-compiling', 'removing',
680 | 'Processing'):
681 | if line.startswith(prefix):
682 | return Logger.DEBUG
683 | return Logger.DEBUG
684 | for prefix in ['Reading ', 'Best match', 'Processing setuptools',
685 | 'Copying setuptools', 'Adding setuptools',
686 | 'Installing ', 'Installed ']:
687 | if line.startswith(prefix):
688 | return Logger.DEBUG
689 | return Logger.INFO
690 |
691 |
692 | class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter):
693 | """
694 | Custom help formatter for use in ConfigOptionParser that updates
695 | the defaults before expanding them, allowing them to show up correctly
696 | in the help listing
697 | """
698 | def expand_default(self, option):
699 | if self.parser is not None:
700 | self.parser.update_defaults(self.parser.defaults)
701 | return optparse.IndentedHelpFormatter.expand_default(self, option)
702 |
703 |
704 | class ConfigOptionParser(optparse.OptionParser):
705 | """
706 | Custom option parser which updates its defaults by by checking the
707 | configuration files and environmental variables
708 | """
709 | def __init__(self, *args, **kwargs):
710 | self.config = ConfigParser.RawConfigParser()
711 | self.files = self.get_config_files()
712 | self.config.read(self.files)
713 | optparse.OptionParser.__init__(self, *args, **kwargs)
714 |
715 | def get_config_files(self):
716 | config_file = os.environ.get('VIRTUALENV_CONFIG_FILE', False)
717 | if config_file and os.path.exists(config_file):
718 | return [config_file]
719 | return [default_config_file]
720 |
721 | def update_defaults(self, defaults):
722 | """
723 | Updates the given defaults with values from the config files and
724 | the environ. Does a little special handling for certain types of
725 | options (lists).
726 | """
727 | # Then go and look for the other sources of configuration:
728 | config = {}
729 | # 1. config files
730 | config.update(dict(self.get_config_section('virtualenv')))
731 | # 2. environmental variables
732 | config.update(dict(self.get_environ_vars()))
733 | # Then set the options with those values
734 | for key, val in config.items():
735 | key = key.replace('_', '-')
736 | if not key.startswith('--'):
737 | key = '--%s' % key # only prefer long opts
738 | option = self.get_option(key)
739 | if option is not None:
740 | # ignore empty values
741 | if not val:
742 | continue
743 | # handle multiline configs
744 | if option.action == 'append':
745 | val = val.split()
746 | else:
747 | option.nargs = 1
748 | if option.action == 'store_false':
749 | val = not strtobool(val)
750 | elif option.action in ('store_true', 'count'):
751 | val = strtobool(val)
752 | try:
753 | val = option.convert_value(key, val)
754 | except optparse.OptionValueError:
755 | e = sys.exc_info()[1]
756 | print("An error occured during configuration: %s" % e)
757 | sys.exit(3)
758 | defaults[option.dest] = val
759 | return defaults
760 |
761 | def get_config_section(self, name):
762 | """
763 | Get a section of a configuration
764 | """
765 | if self.config.has_section(name):
766 | return self.config.items(name)
767 | return []
768 |
769 | def get_environ_vars(self, prefix='VIRTUALENV_'):
770 | """
771 | Returns a generator with all environmental vars with prefix VIRTUALENV
772 | """
773 | for key, val in os.environ.items():
774 | if key.startswith(prefix):
775 | yield (key.replace(prefix, '').lower(), val)
776 |
777 | def get_default_values(self):
778 | """
779 | Overridding to make updating the defaults after instantiation of
780 | the option parser possible, update_defaults() does the dirty work.
781 | """
782 | if not self.process_default_values:
783 | # Old, pre-Optik 1.5 behaviour.
784 | return optparse.Values(self.defaults)
785 |
786 | defaults = self.update_defaults(self.defaults.copy()) # ours
787 | for option in self._get_all_options():
788 | default = defaults.get(option.dest)
789 | if isinstance(default, basestring):
790 | opt_str = option.get_opt_string()
791 | defaults[option.dest] = option.check_value(opt_str, default)
792 | return optparse.Values(defaults)
793 |
794 |
795 | def main():
796 | parser = ConfigOptionParser(
797 | version=virtualenv_version,
798 | usage="%prog [OPTIONS] DEST_DIR",
799 | formatter=UpdatingDefaultsHelpFormatter())
800 |
801 | parser.add_option(
802 | '-v', '--verbose',
803 | action='count',
804 | dest='verbose',
805 | default=0,
806 | help="Increase verbosity")
807 |
808 | parser.add_option(
809 | '-q', '--quiet',
810 | action='count',
811 | dest='quiet',
812 | default=0,
813 | help='Decrease verbosity')
814 |
815 | parser.add_option(
816 | '-p', '--python',
817 | dest='python',
818 | metavar='PYTHON_EXE',
819 | help='The Python interpreter to use, e.g., --python=python2.5 will use the python2.5 '
820 | 'interpreter to create the new environment. The default is the interpreter that '
821 | 'virtualenv was installed with (%s)' % sys.executable)
822 |
823 | parser.add_option(
824 | '--clear',
825 | dest='clear',
826 | action='store_true',
827 | help="Clear out the non-root install and start from scratch")
828 |
829 | parser.set_defaults(system_site_packages=False)
830 | parser.add_option(
831 | '--no-site-packages',
832 | dest='system_site_packages',
833 | action='store_false',
834 | help="Don't give access to the global site-packages dir to the "
835 | "virtual environment (default)")
836 |
837 | parser.add_option(
838 | '--system-site-packages',
839 | dest='system_site_packages',
840 | action='store_true',
841 | help="Give access to the global site-packages dir to the "
842 | "virtual environment")
843 |
844 | parser.add_option(
845 | '--unzip-setuptools',
846 | dest='unzip_setuptools',
847 | action='store_true',
848 | help="Unzip Setuptools or Distribute when installing it")
849 |
850 | parser.add_option(
851 | '--relocatable',
852 | dest='relocatable',
853 | action='store_true',
854 | help='Make an EXISTING virtualenv environment relocatable. '
855 | 'This fixes up scripts and makes all .pth files relative')
856 |
857 | parser.add_option(
858 | '--distribute', '--use-distribute', # the second option is for legacy reasons here. Hi Kenneth!
859 | dest='use_distribute',
860 | action='store_true',
861 | help='Use Distribute instead of Setuptools. Set environ variable '
862 | 'VIRTUALENV_DISTRIBUTE to make it the default ')
863 |
864 | parser.add_option(
865 | '--no-setuptools',
866 | dest='no_setuptools',
867 | action='store_true',
868 | help='Do not install distribute/setuptools (or pip) '
869 | 'in the new virtualenv.')
870 |
871 | parser.add_option(
872 | '--no-pip',
873 | dest='no_pip',
874 | action='store_true',
875 | help='Do not install pip in the new virtualenv.')
876 |
877 | parser.add_option(
878 | '--setuptools',
879 | dest='use_distribute',
880 | action='store_false',
881 | help='Use Setuptools instead of Distribute. Set environ variable '
882 | 'VIRTUALENV_SETUPTOOLS to make it the default ')
883 |
884 | # Set this to True to use distribute by default, even in Python 2.
885 | parser.set_defaults(use_distribute=False)
886 |
887 | default_search_dirs = file_search_dirs()
888 | parser.add_option(
889 | '--extra-search-dir',
890 | dest="search_dirs",
891 | action="append",
892 | default=default_search_dirs,
893 | help="Directory to look for setuptools/distribute/pip distributions in. "
894 | "You can add any number of additional --extra-search-dir paths.")
895 |
896 | parser.add_option(
897 | '--never-download',
898 | dest="never_download",
899 | action="store_true",
900 | help="Never download anything from the network. Instead, virtualenv will fail "
901 | "if local distributions of setuptools/distribute/pip are not present.")
902 |
903 | parser.add_option(
904 | '--prompt',
905 | dest='prompt',
906 | help='Provides an alternative prompt prefix for this environment')
907 |
908 | if 'extend_parser' in globals():
909 | extend_parser(parser)
910 |
911 | options, args = parser.parse_args()
912 |
913 | global logger
914 |
915 | if 'adjust_options' in globals():
916 | adjust_options(options, args)
917 |
918 | verbosity = options.verbose - options.quiet
919 | logger = Logger([(Logger.level_for_integer(2 - verbosity), sys.stdout)])
920 |
921 | if options.python and not os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'):
922 | env = os.environ.copy()
923 | interpreter = resolve_interpreter(options.python)
924 | if interpreter == sys.executable:
925 | logger.warn('Already using interpreter %s' % interpreter)
926 | else:
927 | logger.notify('Running virtualenv with interpreter %s' % interpreter)
928 | env['VIRTUALENV_INTERPRETER_RUNNING'] = 'true'
929 | file = __file__
930 | if file.endswith('.pyc'):
931 | file = file[:-1]
932 | popen = subprocess.Popen([interpreter, file] + sys.argv[1:], env=env)
933 | raise SystemExit(popen.wait())
934 |
935 | # Force --distribute on Python 3, since setuptools is not available.
936 | if majver > 2:
937 | options.use_distribute = True
938 |
939 | if os.environ.get('PYTHONDONTWRITEBYTECODE') and not options.use_distribute:
940 | print(
941 | "The PYTHONDONTWRITEBYTECODE environment variable is "
942 | "not compatible with setuptools. Either use --distribute "
943 | "or unset PYTHONDONTWRITEBYTECODE.")
944 | sys.exit(2)
945 | if not args:
946 | print('You must provide a DEST_DIR')
947 | parser.print_help()
948 | sys.exit(2)
949 | if len(args) > 1:
950 | print('There must be only one argument: DEST_DIR (you gave %s)' % (
951 | ' '.join(args)))
952 | parser.print_help()
953 | sys.exit(2)
954 |
955 | home_dir = args[0]
956 |
957 | if os.environ.get('WORKING_ENV'):
958 | logger.fatal('ERROR: you cannot run virtualenv while in a workingenv')
959 | logger.fatal('Please deactivate your workingenv, then re-run this script')
960 | sys.exit(3)
961 |
962 | if 'PYTHONHOME' in os.environ:
963 | logger.warn('PYTHONHOME is set. You *must* activate the virtualenv before using it')
964 | del os.environ['PYTHONHOME']
965 |
966 | if options.relocatable:
967 | make_environment_relocatable(home_dir)
968 | return
969 |
970 | create_environment(home_dir,
971 | site_packages=options.system_site_packages,
972 | clear=options.clear,
973 | unzip_setuptools=options.unzip_setuptools,
974 | use_distribute=options.use_distribute,
975 | prompt=options.prompt,
976 | search_dirs=options.search_dirs,
977 | never_download=options.never_download,
978 | no_setuptools=options.no_setuptools,
979 | no_pip=options.no_pip)
980 | if 'after_install' in globals():
981 | after_install(options, home_dir)
982 |
983 | def call_subprocess(cmd, show_stdout=True,
984 | filter_stdout=None, cwd=None,
985 | raise_on_returncode=True, extra_env=None,
986 | remove_from_env=None):
987 | cmd_parts = []
988 | for part in cmd:
989 | if len(part) > 45:
990 | part = part[:20]+"..."+part[-20:]
991 | if ' ' in part or '\n' in part or '"' in part or "'" in part:
992 | part = '"%s"' % part.replace('"', '\\"')
993 | if hasattr(part, 'decode'):
994 | try:
995 | part = part.decode(sys.getdefaultencoding())
996 | except UnicodeDecodeError:
997 | part = part.decode(sys.getfilesystemencoding())
998 | cmd_parts.append(part)
999 | cmd_desc = ' '.join(cmd_parts)
1000 | if show_stdout:
1001 | stdout = None
1002 | else:
1003 | stdout = subprocess.PIPE
1004 | logger.debug("Running command %s" % cmd_desc)
1005 | if extra_env or remove_from_env:
1006 | env = os.environ.copy()
1007 | if extra_env:
1008 | env.update(extra_env)
1009 | if remove_from_env:
1010 | for varname in remove_from_env:
1011 | env.pop(varname, None)
1012 | else:
1013 | env = None
1014 | try:
1015 | proc = subprocess.Popen(
1016 | cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout,
1017 | cwd=cwd, env=env)
1018 | except Exception:
1019 | e = sys.exc_info()[1]
1020 | logger.fatal(
1021 | "Error %s while executing command %s" % (e, cmd_desc))
1022 | raise
1023 | all_output = []
1024 | if stdout is not None:
1025 | stdout = proc.stdout
1026 | encoding = sys.getdefaultencoding()
1027 | fs_encoding = sys.getfilesystemencoding()
1028 | while 1:
1029 | line = stdout.readline()
1030 | try:
1031 | line = line.decode(encoding)
1032 | except UnicodeDecodeError:
1033 | line = line.decode(fs_encoding)
1034 | if not line:
1035 | break
1036 | line = line.rstrip()
1037 | all_output.append(line)
1038 | if filter_stdout:
1039 | level = filter_stdout(line)
1040 | if isinstance(level, tuple):
1041 | level, line = level
1042 | logger.log(level, line)
1043 | if not logger.stdout_level_matches(level):
1044 | logger.show_progress()
1045 | else:
1046 | logger.info(line)
1047 | else:
1048 | proc.communicate()
1049 | proc.wait()
1050 | if proc.returncode:
1051 | if raise_on_returncode:
1052 | if all_output:
1053 | logger.notify('Complete output from command %s:' % cmd_desc)
1054 | logger.notify('\n'.join(all_output) + '\n----------------------------------------')
1055 | raise OSError(
1056 | "Command %s failed with error code %s"
1057 | % (cmd_desc, proc.returncode))
1058 | else:
1059 | logger.warn(
1060 | "Command %s had error code %s"
1061 | % (cmd_desc, proc.returncode))
1062 |
1063 |
1064 | def create_environment(home_dir, site_packages=False, clear=False,
1065 | unzip_setuptools=False, use_distribute=False,
1066 | prompt=None, search_dirs=None, never_download=False,
1067 | no_setuptools=False, no_pip=False):
1068 | """
1069 | Creates a new environment in ``home_dir``.
1070 |
1071 | If ``site_packages`` is true, then the global ``site-packages/``
1072 | directory will be on the path.
1073 |
1074 | If ``clear`` is true (default False) then the environment will
1075 | first be cleared.
1076 | """
1077 | home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
1078 |
1079 | py_executable = os.path.abspath(install_python(
1080 | home_dir, lib_dir, inc_dir, bin_dir,
1081 | site_packages=site_packages, clear=clear))
1082 |
1083 | install_distutils(home_dir)
1084 |
1085 | if not no_setuptools:
1086 | if use_distribute:
1087 | install_distribute(py_executable, unzip=unzip_setuptools,
1088 | search_dirs=search_dirs, never_download=never_download)
1089 | else:
1090 | install_setuptools(py_executable, unzip=unzip_setuptools,
1091 | search_dirs=search_dirs, never_download=never_download)
1092 |
1093 | if not no_pip:
1094 | install_pip(py_executable, search_dirs=search_dirs, never_download=never_download)
1095 |
1096 | install_activate(home_dir, bin_dir, prompt)
1097 |
1098 | def is_executable_file(fpath):
1099 | return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
1100 |
1101 | def path_locations(home_dir):
1102 | """Return the path locations for the environment (where libraries are,
1103 | where scripts go, etc)"""
1104 | # XXX: We'd use distutils.sysconfig.get_python_inc/lib but its
1105 | # prefix arg is broken: http://bugs.python.org/issue3386
1106 | if is_win:
1107 | # Windows has lots of problems with executables with spaces in
1108 | # the name; this function will remove them (using the ~1
1109 | # format):
1110 | mkdir(home_dir)
1111 | if ' ' in home_dir:
1112 | import ctypes
1113 | GetShortPathName = ctypes.windll.kernel32.GetShortPathNameW
1114 | size = max(len(home_dir)+1, 256)
1115 | buf = ctypes.create_unicode_buffer(size)
1116 | try:
1117 | u = unicode
1118 | except NameError:
1119 | u = str
1120 | ret = GetShortPathName(u(home_dir), buf, size)
1121 | if not ret:
1122 | print('Error: the path "%s" has a space in it' % home_dir)
1123 | print('We could not determine the short pathname for it.')
1124 | print('Exiting.')
1125 | sys.exit(3)
1126 | home_dir = str(buf.value)
1127 | lib_dir = join(home_dir, 'Lib')
1128 | inc_dir = join(home_dir, 'Include')
1129 | bin_dir = join(home_dir, 'Scripts')
1130 | if is_jython:
1131 | lib_dir = join(home_dir, 'Lib')
1132 | inc_dir = join(home_dir, 'Include')
1133 | bin_dir = join(home_dir, 'bin')
1134 | elif is_pypy:
1135 | lib_dir = home_dir
1136 | inc_dir = join(home_dir, 'include')
1137 | bin_dir = join(home_dir, 'bin')
1138 | elif not is_win:
1139 | lib_dir = join(home_dir, 'lib', py_version)
1140 | multiarch_exec = '/usr/bin/multiarch-platform'
1141 | if is_executable_file(multiarch_exec):
1142 | # In Mageia (2) and Mandriva distros the include dir must be like:
1143 | # virtualenv/include/multiarch-x86_64-linux/python2.7
1144 | # instead of being virtualenv/include/python2.7
1145 | p = subprocess.Popen(multiarch_exec, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1146 | stdout, stderr = p.communicate()
1147 | # stdout.strip is needed to remove newline character
1148 | inc_dir = join(home_dir, 'include', stdout.strip(), py_version + abiflags)
1149 | else:
1150 | inc_dir = join(home_dir, 'include', py_version + abiflags)
1151 | bin_dir = join(home_dir, 'bin')
1152 | return home_dir, lib_dir, inc_dir, bin_dir
1153 |
1154 |
1155 | def change_prefix(filename, dst_prefix):
1156 | prefixes = [sys.prefix]
1157 |
1158 | if is_darwin:
1159 | prefixes.extend((
1160 | os.path.join("/Library/Python", sys.version[:3], "site-packages"),
1161 | os.path.join(sys.prefix, "Extras", "lib", "python"),
1162 | os.path.join("~", "Library", "Python", sys.version[:3], "site-packages"),
1163 | # Python 2.6 no-frameworks
1164 | os.path.join("~", ".local", "lib","python", sys.version[:3], "site-packages"),
1165 | # System Python 2.7 on OSX Mountain Lion
1166 | os.path.join("~", "Library", "Python", sys.version[:3], "lib", "python", "site-packages")))
1167 |
1168 | if hasattr(sys, 'real_prefix'):
1169 | prefixes.append(sys.real_prefix)
1170 | if hasattr(sys, 'base_prefix'):
1171 | prefixes.append(sys.base_prefix)
1172 | prefixes = list(map(os.path.expanduser, prefixes))
1173 | prefixes = list(map(os.path.abspath, prefixes))
1174 | # Check longer prefixes first so we don't split in the middle of a filename
1175 | prefixes = sorted(prefixes, key=len, reverse=True)
1176 | filename = os.path.abspath(filename)
1177 | for src_prefix in prefixes:
1178 | if filename.startswith(src_prefix):
1179 | _, relpath = filename.split(src_prefix, 1)
1180 | if src_prefix != os.sep: # sys.prefix == "/"
1181 | assert relpath[0] == os.sep
1182 | relpath = relpath[1:]
1183 | return join(dst_prefix, relpath)
1184 | assert False, "Filename %s does not start with any of these prefixes: %s" % \
1185 | (filename, prefixes)
1186 |
1187 | def copy_required_modules(dst_prefix):
1188 | import imp
1189 | # If we are running under -p, we need to remove the current
1190 | # directory from sys.path temporarily here, so that we
1191 | # definitely get the modules from the site directory of
1192 | # the interpreter we are running under, not the one
1193 | # virtualenv.py is installed under (which might lead to py2/py3
1194 | # incompatibility issues)
1195 | _prev_sys_path = sys.path
1196 | if os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'):
1197 | sys.path = sys.path[1:]
1198 | try:
1199 | for modname in REQUIRED_MODULES:
1200 | if modname in sys.builtin_module_names:
1201 | logger.info("Ignoring built-in bootstrap module: %s" % modname)
1202 | continue
1203 | try:
1204 | f, filename, _ = imp.find_module(modname)
1205 | except ImportError:
1206 | logger.info("Cannot import bootstrap module: %s" % modname)
1207 | else:
1208 | if f is not None:
1209 | f.close()
1210 | # special-case custom readline.so on OS X, but not for pypy:
1211 | if modname == 'readline' and sys.platform == 'darwin' and not (
1212 | is_pypy or filename.endswith(join('lib-dynload', 'readline.so'))):
1213 | dst_filename = join(dst_prefix, 'lib', 'python%s' % sys.version[:3], 'readline.so')
1214 | else:
1215 | dst_filename = change_prefix(filename, dst_prefix)
1216 | copyfile(filename, dst_filename)
1217 | if filename.endswith('.pyc'):
1218 | pyfile = filename[:-1]
1219 | if os.path.exists(pyfile):
1220 | copyfile(pyfile, dst_filename[:-1])
1221 | finally:
1222 | sys.path = _prev_sys_path
1223 |
1224 |
1225 | def subst_path(prefix_path, prefix, home_dir):
1226 | prefix_path = os.path.normpath(prefix_path)
1227 | prefix = os.path.normpath(prefix)
1228 | home_dir = os.path.normpath(home_dir)
1229 | if not prefix_path.startswith(prefix):
1230 | logger.warn('Path not in prefix %r %r', prefix_path, prefix)
1231 | return
1232 | return prefix_path.replace(prefix, home_dir, 1)
1233 |
1234 |
1235 | def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear):
1236 | """Install just the base environment, no distutils patches etc"""
1237 | if sys.executable.startswith(bin_dir):
1238 | print('Please use the *system* python to run this script')
1239 | return
1240 |
1241 | if clear:
1242 | rmtree(lib_dir)
1243 | ## FIXME: why not delete it?
1244 | ## Maybe it should delete everything with #!/path/to/venv/python in it
1245 | logger.notify('Not deleting %s', bin_dir)
1246 |
1247 | if hasattr(sys, 'real_prefix'):
1248 | logger.notify('Using real prefix %r' % sys.real_prefix)
1249 | prefix = sys.real_prefix
1250 | elif hasattr(sys, 'base_prefix'):
1251 | logger.notify('Using base prefix %r' % sys.base_prefix)
1252 | prefix = sys.base_prefix
1253 | else:
1254 | prefix = sys.prefix
1255 | mkdir(lib_dir)
1256 | fix_lib64(lib_dir)
1257 | stdlib_dirs = [os.path.dirname(os.__file__)]
1258 | if is_win:
1259 | stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), 'DLLs'))
1260 | elif is_darwin:
1261 | stdlib_dirs.append(join(stdlib_dirs[0], 'site-packages'))
1262 | if hasattr(os, 'symlink'):
1263 | logger.info('Symlinking Python bootstrap modules')
1264 | else:
1265 | logger.info('Copying Python bootstrap modules')
1266 | logger.indent += 2
1267 | try:
1268 | # copy required files...
1269 | for stdlib_dir in stdlib_dirs:
1270 | if not os.path.isdir(stdlib_dir):
1271 | continue
1272 | for fn in os.listdir(stdlib_dir):
1273 | bn = os.path.splitext(fn)[0]
1274 | if fn != 'site-packages' and bn in REQUIRED_FILES:
1275 | copyfile(join(stdlib_dir, fn), join(lib_dir, fn))
1276 | # ...and modules
1277 | copy_required_modules(home_dir)
1278 | finally:
1279 | logger.indent -= 2
1280 | mkdir(join(lib_dir, 'site-packages'))
1281 | import site
1282 | site_filename = site.__file__
1283 | if site_filename.endswith('.pyc'):
1284 | site_filename = site_filename[:-1]
1285 | elif site_filename.endswith('$py.class'):
1286 | site_filename = site_filename.replace('$py.class', '.py')
1287 | site_filename_dst = change_prefix(site_filename, home_dir)
1288 | site_dir = os.path.dirname(site_filename_dst)
1289 | writefile(site_filename_dst, SITE_PY)
1290 | writefile(join(site_dir, 'orig-prefix.txt'), prefix)
1291 | site_packages_filename = join(site_dir, 'no-global-site-packages.txt')
1292 | if not site_packages:
1293 | writefile(site_packages_filename, '')
1294 |
1295 | if is_pypy or is_win:
1296 | stdinc_dir = join(prefix, 'include')
1297 | else:
1298 | stdinc_dir = join(prefix, 'include', py_version + abiflags)
1299 | if os.path.exists(stdinc_dir):
1300 | copyfile(stdinc_dir, inc_dir)
1301 | else:
1302 | logger.debug('No include dir %s' % stdinc_dir)
1303 |
1304 | platinc_dir = distutils.sysconfig.get_python_inc(plat_specific=1)
1305 | if platinc_dir != stdinc_dir:
1306 | platinc_dest = distutils.sysconfig.get_python_inc(
1307 | plat_specific=1, prefix=home_dir)
1308 | if platinc_dir == platinc_dest:
1309 | # Do platinc_dest manually due to a CPython bug;
1310 | # not http://bugs.python.org/issue3386 but a close cousin
1311 | platinc_dest = subst_path(platinc_dir, prefix, home_dir)
1312 | if platinc_dest:
1313 | # PyPy's stdinc_dir and prefix are relative to the original binary
1314 | # (traversing virtualenvs), whereas the platinc_dir is relative to
1315 | # the inner virtualenv and ignores the prefix argument.
1316 | # This seems more evolved than designed.
1317 | copyfile(platinc_dir, platinc_dest)
1318 |
1319 | # pypy never uses exec_prefix, just ignore it
1320 | if sys.exec_prefix != prefix and not is_pypy:
1321 | if is_win:
1322 | exec_dir = join(sys.exec_prefix, 'lib')
1323 | elif is_jython:
1324 | exec_dir = join(sys.exec_prefix, 'Lib')
1325 | else:
1326 | exec_dir = join(sys.exec_prefix, 'lib', py_version)
1327 | for fn in os.listdir(exec_dir):
1328 | copyfile(join(exec_dir, fn), join(lib_dir, fn))
1329 |
1330 | if is_jython:
1331 | # Jython has either jython-dev.jar and javalib/ dir, or just
1332 | # jython.jar
1333 | for name in 'jython-dev.jar', 'javalib', 'jython.jar':
1334 | src = join(prefix, name)
1335 | if os.path.exists(src):
1336 | copyfile(src, join(home_dir, name))
1337 | # XXX: registry should always exist after Jython 2.5rc1
1338 | src = join(prefix, 'registry')
1339 | if os.path.exists(src):
1340 | copyfile(src, join(home_dir, 'registry'), symlink=False)
1341 | copyfile(join(prefix, 'cachedir'), join(home_dir, 'cachedir'),
1342 | symlink=False)
1343 |
1344 | mkdir(bin_dir)
1345 | py_executable = join(bin_dir, os.path.basename(sys.executable))
1346 | if 'Python.framework' in prefix:
1347 | # OS X framework builds cause validation to break
1348 | # https://github.com/pypa/virtualenv/issues/322
1349 | if os.environ.get('__PYVENV_LAUNCHER__'):
1350 | os.unsetenv('__PYVENV_LAUNCHER__')
1351 | if re.search(r'/Python(?:-32|-64)*$', py_executable):
1352 | # The name of the python executable is not quite what
1353 | # we want, rename it.
1354 | py_executable = os.path.join(
1355 | os.path.dirname(py_executable), 'python')
1356 |
1357 | logger.notify('New %s executable in %s', expected_exe, py_executable)
1358 | pcbuild_dir = os.path.dirname(sys.executable)
1359 | pyd_pth = os.path.join(lib_dir, 'site-packages', 'virtualenv_builddir_pyd.pth')
1360 | if is_win and os.path.exists(os.path.join(pcbuild_dir, 'build.bat')):
1361 | logger.notify('Detected python running from build directory %s', pcbuild_dir)
1362 | logger.notify('Writing .pth file linking to build directory for *.pyd files')
1363 | writefile(pyd_pth, pcbuild_dir)
1364 | else:
1365 | pcbuild_dir = None
1366 | if os.path.exists(pyd_pth):
1367 | logger.info('Deleting %s (not Windows env or not build directory python)' % pyd_pth)
1368 | os.unlink(pyd_pth)
1369 |
1370 | if sys.executable != py_executable:
1371 | ## FIXME: could I just hard link?
1372 | executable = sys.executable
1373 | shutil.copyfile(executable, py_executable)
1374 | make_exe(py_executable)
1375 | if is_win or is_cygwin:
1376 | pythonw = os.path.join(os.path.dirname(sys.executable), 'pythonw.exe')
1377 | if os.path.exists(pythonw):
1378 | logger.info('Also created pythonw.exe')
1379 | shutil.copyfile(pythonw, os.path.join(os.path.dirname(py_executable), 'pythonw.exe'))
1380 | python_d = os.path.join(os.path.dirname(sys.executable), 'python_d.exe')
1381 | python_d_dest = os.path.join(os.path.dirname(py_executable), 'python_d.exe')
1382 | if os.path.exists(python_d):
1383 | logger.info('Also created python_d.exe')
1384 | shutil.copyfile(python_d, python_d_dest)
1385 | elif os.path.exists(python_d_dest):
1386 | logger.info('Removed python_d.exe as it is no longer at the source')
1387 | os.unlink(python_d_dest)
1388 | # we need to copy the DLL to enforce that windows will load the correct one.
1389 | # may not exist if we are cygwin.
1390 | py_executable_dll = 'python%s%s.dll' % (
1391 | sys.version_info[0], sys.version_info[1])
1392 | py_executable_dll_d = 'python%s%s_d.dll' % (
1393 | sys.version_info[0], sys.version_info[1])
1394 | pythondll = os.path.join(os.path.dirname(sys.executable), py_executable_dll)
1395 | pythondll_d = os.path.join(os.path.dirname(sys.executable), py_executable_dll_d)
1396 | pythondll_d_dest = os.path.join(os.path.dirname(py_executable), py_executable_dll_d)
1397 | if os.path.exists(pythondll):
1398 | logger.info('Also created %s' % py_executable_dll)
1399 | shutil.copyfile(pythondll, os.path.join(os.path.dirname(py_executable), py_executable_dll))
1400 | if os.path.exists(pythondll_d):
1401 | logger.info('Also created %s' % py_executable_dll_d)
1402 | shutil.copyfile(pythondll_d, pythondll_d_dest)
1403 | elif os.path.exists(pythondll_d_dest):
1404 | logger.info('Removed %s as the source does not exist' % pythondll_d_dest)
1405 | os.unlink(pythondll_d_dest)
1406 | if is_pypy:
1407 | # make a symlink python --> pypy-c
1408 | python_executable = os.path.join(os.path.dirname(py_executable), 'python')
1409 | if sys.platform in ('win32', 'cygwin'):
1410 | python_executable += '.exe'
1411 | logger.info('Also created executable %s' % python_executable)
1412 | copyfile(py_executable, python_executable)
1413 |
1414 | if is_win:
1415 | for name in 'libexpat.dll', 'libpypy.dll', 'libpypy-c.dll', 'libeay32.dll', 'ssleay32.dll', 'sqlite.dll':
1416 | src = join(prefix, name)
1417 | if os.path.exists(src):
1418 | copyfile(src, join(bin_dir, name))
1419 |
1420 | if os.path.splitext(os.path.basename(py_executable))[0] != expected_exe:
1421 | secondary_exe = os.path.join(os.path.dirname(py_executable),
1422 | expected_exe)
1423 | py_executable_ext = os.path.splitext(py_executable)[1]
1424 | if py_executable_ext == '.exe':
1425 | # python2.4 gives an extension of '.4' :P
1426 | secondary_exe += py_executable_ext
1427 | if os.path.exists(secondary_exe):
1428 | logger.warn('Not overwriting existing %s script %s (you must use %s)'
1429 | % (expected_exe, secondary_exe, py_executable))
1430 | else:
1431 | logger.notify('Also creating executable in %s' % secondary_exe)
1432 | shutil.copyfile(sys.executable, secondary_exe)
1433 | make_exe(secondary_exe)
1434 |
1435 | if '.framework' in prefix:
1436 | if 'Python.framework' in prefix:
1437 | logger.debug('MacOSX Python framework detected')
1438 | # Make sure we use the the embedded interpreter inside
1439 | # the framework, even if sys.executable points to
1440 | # the stub executable in ${sys.prefix}/bin
1441 | # See http://groups.google.com/group/python-virtualenv/
1442 | # browse_thread/thread/17cab2f85da75951
1443 | original_python = os.path.join(
1444 | prefix, 'Resources/Python.app/Contents/MacOS/Python')
1445 | if 'EPD' in prefix:
1446 | logger.debug('EPD framework detected')
1447 | original_python = os.path.join(prefix, 'bin/python')
1448 | shutil.copy(original_python, py_executable)
1449 |
1450 | # Copy the framework's dylib into the virtual
1451 | # environment
1452 | virtual_lib = os.path.join(home_dir, '.Python')
1453 |
1454 | if os.path.exists(virtual_lib):
1455 | os.unlink(virtual_lib)
1456 | copyfile(
1457 | os.path.join(prefix, 'Python'),
1458 | virtual_lib)
1459 |
1460 | # And then change the install_name of the copied python executable
1461 | try:
1462 | mach_o_change(py_executable,
1463 | os.path.join(prefix, 'Python'),
1464 | '@executable_path/../.Python')
1465 | except:
1466 | e = sys.exc_info()[1]
1467 | logger.warn("Could not call mach_o_change: %s. "
1468 | "Trying to call install_name_tool instead." % e)
1469 | try:
1470 | call_subprocess(
1471 | ["install_name_tool", "-change",
1472 | os.path.join(prefix, 'Python'),
1473 | '@executable_path/../.Python',
1474 | py_executable])
1475 | except:
1476 | logger.fatal("Could not call install_name_tool -- you must "
1477 | "have Apple's development tools installed")
1478 | raise
1479 |
1480 | if not is_win:
1481 | # Ensure that 'python', 'pythonX' and 'pythonX.Y' all exist
1482 | py_exe_version_major = 'python%s' % sys.version_info[0]
1483 | py_exe_version_major_minor = 'python%s.%s' % (
1484 | sys.version_info[0], sys.version_info[1])
1485 | py_exe_no_version = 'python'
1486 | required_symlinks = [ py_exe_no_version, py_exe_version_major,
1487 | py_exe_version_major_minor ]
1488 |
1489 | py_executable_base = os.path.basename(py_executable)
1490 |
1491 | if py_executable_base in required_symlinks:
1492 | # Don't try to symlink to yourself.
1493 | required_symlinks.remove(py_executable_base)
1494 |
1495 | for pth in required_symlinks:
1496 | full_pth = join(bin_dir, pth)
1497 | if os.path.exists(full_pth):
1498 | os.unlink(full_pth)
1499 | os.symlink(py_executable_base, full_pth)
1500 |
1501 | if is_win and ' ' in py_executable:
1502 | # There's a bug with subprocess on Windows when using a first
1503 | # argument that has a space in it. Instead we have to quote
1504 | # the value:
1505 | py_executable = '"%s"' % py_executable
1506 | # NOTE: keep this check as one line, cmd.exe doesn't cope with line breaks
1507 | cmd = [py_executable, '-c', 'import sys;out=sys.stdout;'
1508 | 'getattr(out, "buffer", out).write(sys.prefix.encode("utf-8"))']
1509 | logger.info('Testing executable with %s %s "%s"' % tuple(cmd))
1510 | try:
1511 | proc = subprocess.Popen(cmd,
1512 | stdout=subprocess.PIPE)
1513 | proc_stdout, proc_stderr = proc.communicate()
1514 | except OSError:
1515 | e = sys.exc_info()[1]
1516 | if e.errno == errno.EACCES:
1517 | logger.fatal('ERROR: The executable %s could not be run: %s' % (py_executable, e))
1518 | sys.exit(100)
1519 | else:
1520 | raise e
1521 |
1522 | proc_stdout = proc_stdout.strip().decode("utf-8")
1523 | proc_stdout = os.path.normcase(os.path.abspath(proc_stdout))
1524 | norm_home_dir = os.path.normcase(os.path.abspath(home_dir))
1525 | if hasattr(norm_home_dir, 'decode'):
1526 | norm_home_dir = norm_home_dir.decode(sys.getfilesystemencoding())
1527 | if proc_stdout != norm_home_dir:
1528 | logger.fatal(
1529 | 'ERROR: The executable %s is not functioning' % py_executable)
1530 | logger.fatal(
1531 | 'ERROR: It thinks sys.prefix is %r (should be %r)'
1532 | % (proc_stdout, norm_home_dir))
1533 | logger.fatal(
1534 | 'ERROR: virtualenv is not compatible with this system or executable')
1535 | if is_win:
1536 | logger.fatal(
1537 | 'Note: some Windows users have reported this error when they '
1538 | 'installed Python for "Only this user" or have multiple '
1539 | 'versions of Python installed. Copying the appropriate '
1540 | 'PythonXX.dll to the virtualenv Scripts/ directory may fix '
1541 | 'this problem.')
1542 | sys.exit(100)
1543 | else:
1544 | logger.info('Got sys.prefix result: %r' % proc_stdout)
1545 |
1546 | pydistutils = os.path.expanduser('~/.pydistutils.cfg')
1547 | if os.path.exists(pydistutils):
1548 | logger.notify('Please make sure you remove any previous custom paths from '
1549 | 'your %s file.' % pydistutils)
1550 | ## FIXME: really this should be calculated earlier
1551 |
1552 | fix_local_scheme(home_dir)
1553 |
1554 | if site_packages:
1555 | if os.path.exists(site_packages_filename):
1556 | logger.info('Deleting %s' % site_packages_filename)
1557 | os.unlink(site_packages_filename)
1558 |
1559 | return py_executable
1560 |
1561 |
1562 | def install_activate(home_dir, bin_dir, prompt=None):
1563 | home_dir = os.path.abspath(home_dir)
1564 | if is_win or is_jython and os._name == 'nt':
1565 | files = {
1566 | 'activate.bat': ACTIVATE_BAT,
1567 | 'deactivate.bat': DEACTIVATE_BAT,
1568 | 'activate.ps1': ACTIVATE_PS,
1569 | }
1570 |
1571 | # MSYS needs paths of the form /c/path/to/file
1572 | drive, tail = os.path.splitdrive(home_dir.replace(os.sep, '/'))
1573 | home_dir_msys = (drive and "/%s%s" or "%s%s") % (drive[:1], tail)
1574 |
1575 | # Run-time conditional enables (basic) Cygwin compatibility
1576 | home_dir_sh = ("""$(if [ "$OSTYPE" "==" "cygwin" ]; then cygpath -u '%s'; else echo '%s'; fi;)""" %
1577 | (home_dir, home_dir_msys))
1578 | files['activate'] = ACTIVATE_SH.replace('__VIRTUAL_ENV__', home_dir_sh)
1579 |
1580 | else:
1581 | files = {'activate': ACTIVATE_SH}
1582 |
1583 | # suppling activate.fish in addition to, not instead of, the
1584 | # bash script support.
1585 | files['activate.fish'] = ACTIVATE_FISH
1586 |
1587 | # same for csh/tcsh support...
1588 | files['activate.csh'] = ACTIVATE_CSH
1589 |
1590 | files['activate_this.py'] = ACTIVATE_THIS
1591 | if hasattr(home_dir, 'decode'):
1592 | home_dir = home_dir.decode(sys.getfilesystemencoding())
1593 | vname = os.path.basename(home_dir)
1594 | for name, content in files.items():
1595 | content = content.replace('__VIRTUAL_PROMPT__', prompt or '')
1596 | content = content.replace('__VIRTUAL_WINPROMPT__', prompt or '(%s)' % vname)
1597 | content = content.replace('__VIRTUAL_ENV__', home_dir)
1598 | content = content.replace('__VIRTUAL_NAME__', vname)
1599 | content = content.replace('__BIN_NAME__', os.path.basename(bin_dir))
1600 | writefile(os.path.join(bin_dir, name), content)
1601 |
1602 | def install_distutils(home_dir):
1603 | distutils_path = change_prefix(distutils.__path__[0], home_dir)
1604 | mkdir(distutils_path)
1605 | ## FIXME: maybe this prefix setting should only be put in place if
1606 | ## there's a local distutils.cfg with a prefix setting?
1607 | home_dir = os.path.abspath(home_dir)
1608 | ## FIXME: this is breaking things, removing for now:
1609 | #distutils_cfg = DISTUTILS_CFG + "\n[install]\nprefix=%s\n" % home_dir
1610 | writefile(os.path.join(distutils_path, '__init__.py'), DISTUTILS_INIT)
1611 | writefile(os.path.join(distutils_path, 'distutils.cfg'), DISTUTILS_CFG, overwrite=False)
1612 |
1613 | def fix_local_scheme(home_dir):
1614 | """
1615 | Platforms that use the "posix_local" install scheme (like Ubuntu with
1616 | Python 2.7) need to be given an additional "local" location, sigh.
1617 | """
1618 | try:
1619 | import sysconfig
1620 | except ImportError:
1621 | pass
1622 | else:
1623 | if sysconfig._get_default_scheme() == 'posix_local':
1624 | local_path = os.path.join(home_dir, 'local')
1625 | if not os.path.exists(local_path):
1626 | os.mkdir(local_path)
1627 | for subdir_name in os.listdir(home_dir):
1628 | if subdir_name == 'local':
1629 | continue
1630 | os.symlink(os.path.abspath(os.path.join(home_dir, subdir_name)), \
1631 | os.path.join(local_path, subdir_name))
1632 |
1633 | def fix_lib64(lib_dir):
1634 | """
1635 | Some platforms (particularly Gentoo on x64) put things in lib64/pythonX.Y
1636 | instead of lib/pythonX.Y. If this is such a platform we'll just create a
1637 | symlink so lib64 points to lib
1638 | """
1639 | if [p for p in distutils.sysconfig.get_config_vars().values()
1640 | if isinstance(p, basestring) and 'lib64' in p]:
1641 | logger.debug('This system uses lib64; symlinking lib64 to lib')
1642 | assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], (
1643 | "Unexpected python lib dir: %r" % lib_dir)
1644 | lib_parent = os.path.dirname(lib_dir)
1645 | top_level = os.path.dirname(lib_parent)
1646 | lib_dir = os.path.join(top_level, 'lib')
1647 | lib64_link = os.path.join(top_level, 'lib64')
1648 | assert os.path.basename(lib_parent) == 'lib', (
1649 | "Unexpected parent dir: %r" % lib_parent)
1650 | if os.path.lexists(lib64_link):
1651 | return
1652 | os.symlink('lib', lib64_link)
1653 |
1654 | def resolve_interpreter(exe):
1655 | """
1656 | If the executable given isn't an absolute path, search $PATH for the interpreter
1657 | """
1658 | if os.path.abspath(exe) != exe:
1659 | paths = os.environ.get('PATH', '').split(os.pathsep)
1660 | for path in paths:
1661 | if os.path.exists(os.path.join(path, exe)):
1662 | exe = os.path.join(path, exe)
1663 | break
1664 | if not os.path.exists(exe):
1665 | logger.fatal('The executable %s (from --python=%s) does not exist' % (exe, exe))
1666 | raise SystemExit(3)
1667 | if not is_executable(exe):
1668 | logger.fatal('The executable %s (from --python=%s) is not executable' % (exe, exe))
1669 | raise SystemExit(3)
1670 | return exe
1671 |
1672 | def is_executable(exe):
1673 | """Checks a file is executable"""
1674 | return os.access(exe, os.X_OK)
1675 |
1676 | ############################################################
1677 | ## Relocating the environment:
1678 |
1679 | def make_environment_relocatable(home_dir):
1680 | """
1681 | Makes the already-existing environment use relative paths, and takes out
1682 | the #!-based environment selection in scripts.
1683 | """
1684 | home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
1685 | activate_this = os.path.join(bin_dir, 'activate_this.py')
1686 | if not os.path.exists(activate_this):
1687 | logger.fatal(
1688 | 'The environment doesn\'t have a file %s -- please re-run virtualenv '
1689 | 'on this environment to update it' % activate_this)
1690 | fixup_scripts(home_dir)
1691 | fixup_pth_and_egg_link(home_dir)
1692 | ## FIXME: need to fix up distutils.cfg
1693 |
1694 | OK_ABS_SCRIPTS = ['python', 'python%s' % sys.version[:3],
1695 | 'activate', 'activate.bat', 'activate_this.py']
1696 |
1697 | def fixup_scripts(home_dir):
1698 | # This is what we expect at the top of scripts:
1699 | shebang = '#!%s/bin/python' % os.path.normcase(os.path.abspath(home_dir))
1700 | # This is what we'll put:
1701 | new_shebang = '#!/usr/bin/env python%s' % sys.version[:3]
1702 | if is_win:
1703 | bin_suffix = 'Scripts'
1704 | else:
1705 | bin_suffix = 'bin'
1706 | bin_dir = os.path.join(home_dir, bin_suffix)
1707 | home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
1708 | for filename in os.listdir(bin_dir):
1709 | filename = os.path.join(bin_dir, filename)
1710 | if not os.path.isfile(filename):
1711 | # ignore subdirs, e.g. .svn ones.
1712 | continue
1713 | f = open(filename, 'rb')
1714 | try:
1715 | try:
1716 | lines = f.read().decode('utf-8').splitlines()
1717 | except UnicodeDecodeError:
1718 | # This is probably a binary program instead
1719 | # of a script, so just ignore it.
1720 | continue
1721 | finally:
1722 | f.close()
1723 | if not lines:
1724 | logger.warn('Script %s is an empty file' % filename)
1725 | continue
1726 | if not lines[0].strip().startswith(shebang):
1727 | if os.path.basename(filename) in OK_ABS_SCRIPTS:
1728 | logger.debug('Cannot make script %s relative' % filename)
1729 | elif lines[0].strip() == new_shebang:
1730 | logger.info('Script %s has already been made relative' % filename)
1731 | else:
1732 | logger.warn('Script %s cannot be made relative (it\'s not a normal script that starts with %s)'
1733 | % (filename, shebang))
1734 | continue
1735 | logger.notify('Making script %s relative' % filename)
1736 | script = relative_script([new_shebang] + lines[1:])
1737 | f = open(filename, 'wb')
1738 | f.write('\n'.join(script).encode('utf-8'))
1739 | f.close()
1740 |
1741 | def relative_script(lines):
1742 | "Return a script that'll work in a relocatable environment."
1743 | activate = "import os; activate_this=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'activate_this.py'); execfile(activate_this, dict(__file__=activate_this)); del os, activate_this"
1744 | # Find the last future statement in the script. If we insert the activation
1745 | # line before a future statement, Python will raise a SyntaxError.
1746 | activate_at = None
1747 | for idx, line in reversed(list(enumerate(lines))):
1748 | if line.split()[:3] == ['from', '__future__', 'import']:
1749 | activate_at = idx + 1
1750 | break
1751 | if activate_at is None:
1752 | # Activate after the shebang.
1753 | activate_at = 1
1754 | return lines[:activate_at] + ['', activate, ''] + lines[activate_at:]
1755 |
1756 | def fixup_pth_and_egg_link(home_dir, sys_path=None):
1757 | """Makes .pth and .egg-link files use relative paths"""
1758 | home_dir = os.path.normcase(os.path.abspath(home_dir))
1759 | if sys_path is None:
1760 | sys_path = sys.path
1761 | for path in sys_path:
1762 | if not path:
1763 | path = '.'
1764 | if not os.path.isdir(path):
1765 | continue
1766 | path = os.path.normcase(os.path.abspath(path))
1767 | if not path.startswith(home_dir):
1768 | logger.debug('Skipping system (non-environment) directory %s' % path)
1769 | continue
1770 | for filename in os.listdir(path):
1771 | filename = os.path.join(path, filename)
1772 | if filename.endswith('.pth'):
1773 | if not os.access(filename, os.W_OK):
1774 | logger.warn('Cannot write .pth file %s, skipping' % filename)
1775 | else:
1776 | fixup_pth_file(filename)
1777 | if filename.endswith('.egg-link'):
1778 | if not os.access(filename, os.W_OK):
1779 | logger.warn('Cannot write .egg-link file %s, skipping' % filename)
1780 | else:
1781 | fixup_egg_link(filename)
1782 |
1783 | def fixup_pth_file(filename):
1784 | lines = []
1785 | prev_lines = []
1786 | f = open(filename)
1787 | prev_lines = f.readlines()
1788 | f.close()
1789 | for line in prev_lines:
1790 | line = line.strip()
1791 | if (not line or line.startswith('#') or line.startswith('import ')
1792 | or os.path.abspath(line) != line):
1793 | lines.append(line)
1794 | else:
1795 | new_value = make_relative_path(filename, line)
1796 | if line != new_value:
1797 | logger.debug('Rewriting path %s as %s (in %s)' % (line, new_value, filename))
1798 | lines.append(new_value)
1799 | if lines == prev_lines:
1800 | logger.info('No changes to .pth file %s' % filename)
1801 | return
1802 | logger.notify('Making paths in .pth file %s relative' % filename)
1803 | f = open(filename, 'w')
1804 | f.write('\n'.join(lines) + '\n')
1805 | f.close()
1806 |
1807 | def fixup_egg_link(filename):
1808 | f = open(filename)
1809 | link = f.readline().strip()
1810 | f.close()
1811 | if os.path.abspath(link) != link:
1812 | logger.debug('Link in %s already relative' % filename)
1813 | return
1814 | new_link = make_relative_path(filename, link)
1815 | logger.notify('Rewriting link %s in %s as %s' % (link, filename, new_link))
1816 | f = open(filename, 'w')
1817 | f.write(new_link)
1818 | f.close()
1819 |
1820 | def make_relative_path(source, dest, dest_is_directory=True):
1821 | """
1822 | Make a filename relative, where the filename is dest, and it is
1823 | being referred to from the filename source.
1824 |
1825 | >>> make_relative_path('/usr/share/something/a-file.pth',
1826 | ... '/usr/share/another-place/src/Directory')
1827 | '../another-place/src/Directory'
1828 | >>> make_relative_path('/usr/share/something/a-file.pth',
1829 | ... '/home/user/src/Directory')
1830 | '../../../home/user/src/Directory'
1831 | >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/')
1832 | './'
1833 | """
1834 | source = os.path.dirname(source)
1835 | if not dest_is_directory:
1836 | dest_filename = os.path.basename(dest)
1837 | dest = os.path.dirname(dest)
1838 | dest = os.path.normpath(os.path.abspath(dest))
1839 | source = os.path.normpath(os.path.abspath(source))
1840 | dest_parts = dest.strip(os.path.sep).split(os.path.sep)
1841 | source_parts = source.strip(os.path.sep).split(os.path.sep)
1842 | while dest_parts and source_parts and dest_parts[0] == source_parts[0]:
1843 | dest_parts.pop(0)
1844 | source_parts.pop(0)
1845 | full_parts = ['..']*len(source_parts) + dest_parts
1846 | if not dest_is_directory:
1847 | full_parts.append(dest_filename)
1848 | if not full_parts:
1849 | # Special case for the current directory (otherwise it'd be '')
1850 | return './'
1851 | return os.path.sep.join(full_parts)
1852 |
1853 |
1854 |
1855 | ############################################################
1856 | ## Bootstrap script creation:
1857 |
1858 | def create_bootstrap_script(extra_text, python_version=''):
1859 | """
1860 | Creates a bootstrap script, which is like this script but with
1861 | extend_parser, adjust_options, and after_install hooks.
1862 |
1863 | This returns a string that (written to disk of course) can be used
1864 | as a bootstrap script with your own customizations. The script
1865 | will be the standard virtualenv.py script, with your extra text
1866 | added (your extra text should be Python code).
1867 |
1868 | If you include these functions, they will be called:
1869 |
1870 | ``extend_parser(optparse_parser)``:
1871 | You can add or remove options from the parser here.
1872 |
1873 | ``adjust_options(options, args)``:
1874 | You can change options here, or change the args (if you accept
1875 | different kinds of arguments, be sure you modify ``args`` so it is
1876 | only ``[DEST_DIR]``).
1877 |
1878 | ``after_install(options, home_dir)``:
1879 |
1880 | After everything is installed, this function is called. This
1881 | is probably the function you are most likely to use. An
1882 | example would be::
1883 |
1884 | def after_install(options, home_dir):
1885 | subprocess.call([join(home_dir, 'bin', 'easy_install'),
1886 | 'MyPackage'])
1887 | subprocess.call([join(home_dir, 'bin', 'my-package-script'),
1888 | 'setup', home_dir])
1889 |
1890 | This example immediately installs a package, and runs a setup
1891 | script from that package.
1892 |
1893 | If you provide something like ``python_version='2.5'`` then the
1894 | script will start with ``#!/usr/bin/env python2.5`` instead of
1895 | ``#!/usr/bin/env python``. You can use this when the script must
1896 | be run with a particular Python version.
1897 | """
1898 | filename = __file__
1899 | if filename.endswith('.pyc'):
1900 | filename = filename[:-1]
1901 | f = codecs.open(filename, 'r', encoding='utf-8')
1902 | content = f.read()
1903 | f.close()
1904 | py_exe = 'python%s' % python_version
1905 | content = (('#!/usr/bin/env %s\n' % py_exe)
1906 | + '## WARNING: This file is generated\n'
1907 | + content)
1908 | return content.replace('##EXT' 'END##', extra_text)
1909 |
1910 | ##EXTEND##
1911 |
1912 | def convert(s):
1913 | b = base64.b64decode(s.encode('ascii'))
1914 | return zlib.decompress(b).decode('utf-8')
1915 |
1916 | ##file site.py
1917 | SITE_PY = convert("""
1918 | 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
2057 | UM3ckC/Q8aP8VLy+mjYY5MxOtAdgjULwf2RtvCc=
2058 | """)
2059 |
2060 | ##file ez_setup.py
2061 | EZ_SETUP_PY = convert("""
2062 | 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
2128 | qL7+C1AkCcw=
2129 | """)
2130 |
2131 | ##file distribute_from_egg.py
2132 | DISTRIBUTE_FROM_EGG_PY = convert("""
2133 | eJw9j8tqAzEMRfcG/4MgmxQyptkGusonZBmGoGTUGYFfWPKE6dfXTkM3gqt7rh47OKP3NMF3SQFW
2134 | LlrRU1zhybpAxoKBlIqcrNnBdRjQP3GTocYfzmNrrCPQPN9iwzpxSQfQhWBi0cL3qtRtYIG/4Mv0
2135 | KApY5hooqrOGQ05FQTaxptF9Fnx16Rq0XofjaE1XGXVxHIWK7j8P8EY/rHndLqQ1a0pe3COFgHFy
2136 | hLLdWkDbi/DeEpCjNb3u/zccT2Ob8gtnwVyI
2137 | """)
2138 |
2139 | ##file distribute_setup.py
2140 | DISTRIBUTE_SETUP_PY = convert("""
2141 | eJztPGtz2ziS3/UrcHK5SOUkxs7MzV25TlOVmTizrs0mKdvZ/ZC4aIiEJI75GpC0ov311403SEp2
2142 | LrMfruq8O7ZENBqNfncDzMm/1ft2W5WT6XT6S1W1TctpTdIM/marrmUkK5uW5jltMwCaXK3JvurI
2143 | jpYtaSvSNYw0rO3qtqryBmBxlJOaJg90w4JGDkb1fk5+75oWAJK8Sxlpt1kzWWc5oocvgIQWDFbl
2144 | LGkrvie7rN2SrJ0TWqaEpqmYgAsibFvVpFrLlTT+i4vJhMDPmleFQ30sxklW1BVvkdrYUivg/Ufh
2145 | bLBDzv7ogCxCSVOzJFtnCXlkvAFmIA126hw/A1Ra7cq8oumkyDiv+JxUXHCJloTmLeMlBZ5qILvj
2146 | uVg0Aai0Ik1FVnvSdHWd77NyM8FN07rmVc0znF7VKAzBj/v7/g7u76PJ5BbZJfibiIURIyO8g88N
2147 | biXhWS22p6QrqKw3nKauPCNUioliXtXoT822a7PcfNubgTYrmP68LgvaJlszxIoa6THfKXe/wo5q
2148 | yhs2mRgB4hqNllxebSaTlu8vrJCbDJVTDn+6ubyOb65uLyfsa8JgZ1fi+SVKQE4xEGRJ3lclc7Dp
2149 | fXQr4HDCmkZqUsrWJJa2ESdFGr6gfNPM5BT8wa+ALIT9R+wrS7qWrnI2n5F/F0MGjgM7eemgjxJg
2150 | eCiwkeWSnE0OEn0CdgCyAcmBkFOyBiFJgsir6Ic/lcgT8kdXtaBr+LgrWNkC69ewfAmqasHgEWKq
2151 | wRsAMQWSHwDMD68Cu6QmCxEy3ObMH1N4Avgf2D6MD4cdtgXT02YakFMEHMApmP6Q2vRnS4FgHXxQ
2152 | KzZ3felUTdTUFIwyhE8f43+8vrqdkx7TyAtXZm8u377+9O42/vvl9c3Vh/ew3vQs+in64cepGfp0
2153 | /Q4fb9u2vnj5st7XWSRFFVV881L5yOZlA34sYS/Tl9ZtvZxObi5vP328/fDh3U389vVfL9/0FkrO
2154 | z6cTF+jjX3+Lr96//YDj0+mXyd9YS1Pa0sXfpbe6IOfR2eQ9uNkLx8InZvS0mdx0RUHBKshX+Jn8
2155 | pSrYogYKxffJ6w4o5+7nBStolssn77KElY0CfcOkfxF48QEQBBI8tKPJZCLUWLmiEFzDCv7OtW+K
2156 | ke3LcDbTRsG+QoxKhLaKcCDhxWBb1OBSgQfa30TFQ4qfwbPjOPiRaEd5GQaXFgkoxWkTzNVkCVjl
2157 | abxLARHow4a1yS5VGIzbEFBgzFuYE7pTBRQVREgnF1U1K/W2LEys9qH27E2OkrxqGIYja6GbShGL
2158 | mzaBwwCAg5FbB6Jq2m6j3wFeETbHhzmol0Pr57O72XAjEosdsAx7X+3IruIPLsc0tEOlEhqGrSGO
2159 | KzNI3hhlD2aufymr1vNogY7wsFygkMPHF65y9DyMXe8GdBgyB1huBy6N7HgFH9OOa9Vxc5vIoaOH
2160 | hTEBzdAzkwJcOFgFoavqkfUnoXJmbVJBGNWu+5UHoPyNfLjOSlh9TJ+k+lncMuRGvGg5Y0bblOGs
2161 | ugzA2WYTwn9zYuynrWIE+3+z+T9gNkKGIv6WBKQ4gugXA+HYDsJaQUh5W04dMqPFH/h7hfEG1UY8
2162 | WuA3+MUdRH+Kksr9Sb3XusdZ0+Wtr1pAiARWTkDLAwyqaRsxbGngNIOc+uqDSJbC4Neqy1MxS/BR
2163 | Wutmg9apbCSFLamkO1T5+9yk4fGKNkxv23mcspzu1arI6L6SKPjABu7FabOo96dpBP9Hzo6mNvBz
2164 | SiwVmGaoLxAD1xVo2MjD87vZ89mjjAYINntxSoQD+z9Ea+/nAJes1j3hjgSgyCKRfPDAjLfh2ZxY
2165 | +at83C/UnKpkpctUnTLEoiBYCsOR8u4VRWrHy17S1uPA0kncRrkhd7BEA+j4CBOW5/8xB+HEa/rA
2166 | lre8Y8b3FlQ4gKaDSnIn0nmho3TVVDmaMfJiYpdwNA1A8G/ocm9Hm1hyiaGvDeqHTQwmJfLIRqTV
2167 | yN+iSrucNVjafTG7CSxX+oBDP+19cUTjrecDSOXc0oa2LQ89QDCUOHWi/mhZgLMVB8frAjHkl+x9
2168 | EOUcbDVlIA4VWmamjM7f4y0OM89jRqT6CuHUsuTn5RTqMrXebISw/j58jCqV/7Uq13mWtP7iDPRE
2169 | 1jOJ8CfhDDxKX3SuXg25j9MhFEIWFO04FN/hAGJ6K3y72FjqtkmcdlL48/IUiqisEaKmj1BCiOrq
2170 | Szkd4sPuT0LLoMVEShk7YN5tsbMhWkKqkwGfeFdifInIx5yBgEbx6W4HJUXFkdQE00JN6DrjTTsH
2171 | 4wQ0o9MDQLzXTocsPjn7CqIR+C/llzL8teMcVsn3EjE55TNA7kUAFmEWi5nFUJml0LI2fOWPsbwZ
2172 | sRDQQdIzOsfCP/c8xR1OwdgselHVw6EC+1vs4VlR5JDNjOq1yXZg1fdV+7bqyvS7zfZJMsdIHKRC
2173 | xxxWnHBGW9b3VzFuTligybJExDoSqL83bImfkdilQpZyxFCkv7FtSWOvIrSa5icYX14lol4SrVnF
2174 | +ayV3caSFkxmjfeK9nvICkVytsIW6iPNMw+7Nr2yK1aMg0lTYcvGLQhc2LIUWbFo45jeKaiBmMLI
2175 | vcePe4KNlxCcRLLVq7MylZET+8qUBC+DWUTuJU/ucUWvOAAHwzjTWaSp5PQqLI3kHgUHzXS1B9EV
2176 | TqoyFf3ZmmKsX7E1+htsxSZtR3PbJRb7a7HUaiMthn9JzuCFIyHUjkMlvhKBiGFrXvXIeY5118Qx
2177 | x9Fw6aB4NTa33fwzRnXAfpSXH0dYp23+iR5QSV824rmXrqIgIRhqLDIFpI8MWHogC9egKsHkCaKD
2178 | fal+r2OuvdRZop1dIM9fP1YZanWNppsacmySM4jqpn4x1iOcfDOd45Z8ny2JUlwKB8Mn5JrR9KUI
2179 | rgQjDORnQDpZgck9zPFUYIdKiOFQ+hbQ5KTiHNyFsL4eMtit0GptLxmez7RMwGsV1j/YKcQMgSeg
2180 | DzTtJVWSjYJoyaw5me5W0wGQygsQmR0bOE0lCVhrJMcAAnQN34MH/CPxDhZ14W07V0gY9pILS1Ay
2181 | 1tUgOOwG3Neq+hquuzJBd6a8oBh2x0XTd05evHjYzY5kxvJIwtYoarq2jDfatdzI58eS5j4s5s1Q
2182 | ao8lzEjtY1bJBtag+e/+1LRpBgP9lSJcByQ9fG4WeQYOAwuYDs+r8XRIlC9YKD0jtbET3lIAeHZO
2183 | 3593WIZKebRGeKJ/Up3VMkO6jzNoVASjad04pKv1rt5qTRdkxegdQjSEOTgM8AFla4P+P0R0o8lD
2184 | Vwt/sZa5NSvlliC265C01k4AMc1UhAAXCg4vVmgBYu16kLVnncCm4YSlJsmy7gS8HyLZa66OtMNe
2185 | +xBuI1axw6qJnfURobFKiPQESDQxasTCTdiNeXsFC9wFY2FUOTzN0/EkcT3moYTSTxzxwHqu23FG
2186 | jNfCM3LNt1FpfreAFHFHhKRpGXBNUlCynY76+BQieBB9ePcmOm3wDA/PhyP8NWgrXyM6GTgxaxLt
2187 | TLlDjVH1l7Fwxq/h2KgiXz+0tBbVIyTiYHSx2/EP65wmbAtmxHSXvJchZA32OYdgPvGfygeIsd5h
2188 | AuR0ahPO3MMKusaaxvNsmOnq+xFOE3qcFKBaHbdH6m+Ic+dut+cF9iMXWHj0A4lefOCHV6AnDy5b
2189 | 1n7pZTlg+6+iOnDvELjr9hgw6SnB36pHVAGWM3kAXXUtZtPolHZ0b01WV1D9TNBhzpxIy1HE9+Sp
2190 | 5jt8sEFCGR4QHXuw0pq8yDSYJN2smjEnI6ezqqeu+DmIGZYXYAe07+HmxKdmVJVOAPOO5KwNGoJq
2191 | b3x6n59GzRS/UdNCtz047zUW1eEB3rvAjw73NIZj8lAw3llfv4etQHp1tOtqBliGucKYVoJPlocC
2192 | wFZNrOLEgRZ9cGNvNaVOAyLo7cR354c8Td+5H4Izrp6uIVE3J+JIgOKKEwARxNzfMT1xYySW+VgI
2193 | AQY8kAOPXhRARVytfg/Nceos0o30GopNqOhkZHyqgeH5NkX4t8zxXK5LLyjlSJ32lBseEbfmju5Z
2194 | DF2QYNX+UTAJjE4FqvDZZzKy2LQbVaHcsSN1JNRYPwgLfPG0Ljx0NWIuafsGt9cjZeABNS+HLnDU
2195 | 90jwI56n78N/RfnLQD6Y5edOJlcx/tIkWSqlvywfM16VaGy9vN4turEc3kJ5R2rGi6xp9M04WUaf
2196 | Ygf0IatroGl6ZBtD+lRuN+rEBcDhPE+KqzWJ3WFxOXoSwYSgnxf12NluHalaDqrHT6WpHhlOI7Cv
2197 | M0/v7ykz7/m7Z7mTycyvWUwEttnliYprEA6TB9TqDL+N1QoHbUVm85e//bZASWI8A6nKz99gK9kg
2198 | Gz8a9A8FqOcGeaunTqA/ULgA8cWD4Zv/6CgrZk94mSc5d8yi/zTTcljhlVBKW8arKDVoL8yIdqwJ
2199 | r4PQ+ots1x6MrSNnkAqz6EnHNWfr7Guoo44NdCbiijCljl8p3zxe9PyRTcbVZUYN+Fl/gJCdsq9O
2200 | DIda6/zizmR1YniuLz2ysisYp/I6pNsjQlB5nVjmf4sFh93KGyFyG/1yAbYBOCJYlbcN9tNRj5cY
2201 | 1CSekQZUW9VKOGJmnWdtGOA6y2D2edE7h3SYoBnoLqZw9Q/DJFVYqEoqRg+Xc1BOeYfzZ8mf8V6Z
2202 | R27zWUAid4d0fiutlkpgb9cwHohTFHs5WR2LYsd6tDc1toqZPWIdUisH6tpX+JuEisNT54xVX08d
2203 | M+CD1wCO9eJOyI4FYFUJkDCSdDj5Nqikc8MprZhkSsNYgYHdPQoetn3E1x2ajF+8qDtYyIbhhpxw
2204 | hJkyTN41EWaR/hm3j/FaHnRjehKJy+u96okzEepxfCnctq+zXqpzu6/ZgF/YjHXOyl5/vPpXEmyp
2205 | s0VqfxlQT1813Xtu7osgbskk2wbjgjohKWuZuk+I8RzvIJigiHqb9jNsc/647JMX6aG+drsvqDhF
2206 | mVwadF03a0ZWUbwQpynSN6J6Ct+YfRXE1rx6zFKWyndVsrWCd9+KaZzWSKquIhZze5qjG61uPeSH
2207 | kjHKxqWgsAFD532CAZE8BBq7hDv0bfJ+PtCyherocAXlZWZgo1KOjXuRUW1pZBMRK1MVRMR9uQOb
2208 | KhfynqMVnkcHWvvhLt+oVPVkRRrgGPO3I00f5yrsYZIOJVEjpBzPqRSJ4aGUFHXO75Z8Q1p6MC89
2209 | 0lvv8cafN+yuu7phzizRrMXBuvSQ4pDb8f4l64vWLwi+V55DeiEmFTUQyZxDgZx2ZbK1mZ190g+e
2210 | 12rE2zhGO1mWinfIJIToSeiXjCRUndWkoPwBbzJUhIrjZ2onrLqNKp6K9BzfaQkWiX8RHhIJvFaU
2211 | s4VqTSzYV/GaGSTQi4KWEMPT4M4geXUICWdJxTWkes9HJJwXP9xhwiIpAFcyNvDKCaV6+OzO9EGw
2212 | Xegms5/9N2vuILnS0yYah7jzNPrSlBGJcxG8YflanhgspxHU+QXDuxjNEqOVPepSl9fF2bqCkAe3
2213 | 4l4FBxFKeeHXRF7b0ne39f7sHRH09vjKX7UrsZIvqhRfDpSRBc84BIDbk7CHoBpJBuotOn2gSGkT
2214 | kXvcQGDu2uCbeoB0zQQhg6vrQKjiAHyEyWpHAfp4mQTTXBBR4JuX4v4N8FOQLFqfGg+eLSj7gOi0
2215 | 2pMNaxWucOZfSlGJX1LVe/c7VH1QW6h7lpKh8gq/BlCMt5cxXQ6APtyZjEOLZZBp6AGM+vl6Yuoc
2216 | WEl4WohVCsQr09Ww6vz3PN6JJsyjR90RauiaoVRZ76aEhYxoDeVuGqo1fCep6VoKbkX46ygg3tHD
2217 | XtGPP/6XTIuSrAD5ifoMCDz7z7MzJ/vL15GSvUYqtd+kK9cM3QEjDbLfpdm1b7eZSf6bhK/m5EeH
2218 | RWhkOJ/xEDCczxHPq9loXZIUtYCJsCUhASN7LtfnGyINJeZxAC6pD8dOXQaIHth+qTUwwhsUoL9I
2219 | c4AEBDNMxAU2eSNbMwiSQnF5BnAZEzZmi7or5IFZYp95Pa1zxj0ixfnnaBNFS9xn0OA6gpBysgXi
2220 | rIwV3tkQsBPnqs8ATLawsyOAuvnqmOz/4iqxVFGcnAP3cyi4z4fFtrio3Svkx65+CGRxutqEoIRT
2221 | 5VvwlUW8RMZ670G5L4aF6k1pGwLE31/MSyL2bVfwpoF6uVbHLGK6NZV+e8gUY6o89r2js7L0aooZ
2222 | iooIK35Nn+elDhjjT4cytKnsHui71g35qF8L/glDNOSjjPeuZ8lL8Tf7pmXFJcbWcydpcgjXTk03
2223 | KLymggtomrVgWpLZPS5/xBEZS+WhE0Sakjkdp8YDF4jELUb1Lnj0QUAJNFy5AgkU0TSNJQ5b72qC
2224 | 8WJr0y4Dl9nwkIo7PcugabH114IrEJBr2uWqPLd3Z7csr5c6PUIbF8wWL5wruZPwGOtnwXOo1Rfz
2225 | FnjX0ZDt3YAMMJNp6SPly+mn63dTS6KmfPTur6Rf/3MDmNTgjVgRmNXN1speCxxXbLUDJai5ztzU
2226 | jlyh60S2Av6onMMYFcUu6qYEjqeuGmnxCw0qKDjGAzedrUZdHft3CoTPvqTNXkFpldL/TsLSV1PZ
2227 | /zn6ipR/wVrbr/fUM4zhy8vHvBF4rExcM8RaLRbtwDhGPsSxepHeZMCCOzDhfwBqDMd7
2228 | """)
2229 |
2230 | ##file activate.sh
2231 | ACTIVATE_SH = convert("""
2232 | eJytVVFvokAQfudXTLEPtTlLeo9tvMSmJpq02hSvl7u2wRUG2QR2DSxSe7n/frOACEVNLlceRHa+
2233 | nfl25pvZDswCnoDPQ4QoTRQsENIEPci4CsBMZBq7CAsuLOYqvmYKTTj3YxnBgiXBudGBjUzBZUJI
2234 | BXEqgCvweIyuCjeG4eF2F5x14bcB9KQiQQWrjSddI1/oQIx6SYYeoFjzWIoIhYI1izlbhJjkKO7D
2235 | M/QEmKfO9O7WeRo/zr4P7pyHwWxkwitcgwpQ5Ej96OX+PmiFwLeVjFUOrNYKaq1Nud3nR2n8nI2m
2236 | k9H0friPTGVsUdptaxGrTEfpNVFEskxpXtUkkCkl1UNF9cgLBkx48J4EXyALuBtAwNYIjF5kcmUU
2237 | abMKmMq1ULoiRbgsDEkTSsKSGFCJ6Z8vY/2xYiSacmtyAfCDdCNTVZoVF8vSTQOoEwSnOrngBkws
2238 | MYGMBMg8/bMBLSYKS7pYEXP0PqT+ZmBT0Xuy+Pplj5yn4aM9nk72JD8/Wi+Gr98sD9eWSMOwkapD
2239 | BbUv91XSvmyVkICt2tmXR4tWmrcUCsjWOpw87YidEC8i0gdTSOFhouJUNxR+4NYBG0MftoCTD9F7
2240 | 2rTtxG3oPwY1b2HncYwhrlmj6Wq924xtGDWqfdNxap+OYxplEurnMVo9RWks+rH8qKEtx7kZT5zJ
2241 | 4H7oOFclrN6uFe+d+nW2aIUsSgs/42EIPuOhXq+jEo3S6tX6w2ilNkDnIpHCWdEQhFgwj9pkk7FN
2242 | l/y5eQvRSIQ5+TrL05lewxWpt/Lbhes5cJF3mLET1MGhcKCF+40tNWnUulxrpojwDo2sObdje3Bz
2243 | N3QeHqf3D7OjEXMVV8LN3ZlvuzoWHqiUcNKHtwNd0IbvPGKYYM31nPKCgkUILw3KL+Y8l7aO1ArS
2244 | Ad37nIU0fCj5NE5gQCuC5sOSu+UdI2NeXg/lFkQIlFpdWVaWZRfvqGiirC9o6liJ9FXGYrSY9mI1
2245 | D/Ncozgn13vJvsznr7DnkJWXsyMH7e42ljdJ+aqNDF1bFnKWFLdj31xtaJYK6EXFgqmV/ymD/ROG
2246 | +n8O9H8f5vsGOWXsL1+1k3g=
2247 | """)
2248 |
2249 | ##file activate.fish
2250 | ACTIVATE_FISH = convert("""
2251 | eJyVVWFv2jAQ/c6vuBoqQVWC9nVSNVGVCaS2VC2rNLWVZZILWAs2s52wVvvxsyEJDrjbmgpK7PP5
2252 | 3bt3d22YLbmGlGcIq1wbmCPkGhPYcLMEEsGciwGLDS+YwSjlekngLFVyBe73GXSXxqw/DwbuTS8x
2253 | yyKpFr1WG15lDjETQhpQuQBuIOEKY5O9tlppLqxHKSDByjVAPwEy+mXtCq5MzjIUBTCRgEKTKwFG
2254 | gpBqxTLYXgN2myspVigMaYF92tZSowGZJf4mFExxNs9Qb614CgZtmH0BpEOn11f0cXI/+za8pnfD
2255 | 2ZjA1sg9zlV/8QvcMhxbNu0QwgYokn/d+n02nt6Opzcjcnx1vXcIoN74O4ymWQXmHURfJw9jenc/
2256 | vbmb0enj6P5+cuVhqlKm3S0u2XRtRbA2QQAhV7VhBF0rsgUX9Ur1rBUXJgVSy8O751k8mzY5OrKH
2257 | RW3eaQhYGTr8hrXO59ALhxQ83mCsDLAid3T72CCSdJhaFE+fXgicXAARUiR2WeVO37gH3oYHzFKo
2258 | 9k7CaPZ1UeNwH1tWuXA4uFKYYcEa8vaKqXl7q1UpygMPhFLvlVKyNzsSM3S2km7UBOl4xweUXk5u
2259 | 6e3wZmQ9leY1XE/Ili670tr9g/5POBBpGIJXCCF79L1siarl/dbESa8mD8PL61GpzqpzuMS7tqeB
2260 | 1YkALrRBloBMbR9yLcVx7frQAgUqR7NZIuzkEu110gbNit1enNs82Rx5utq7Z3prU78HFRgulqNC
2261 | OTwbqJa9vkJFclQgZSjbKeBgSsUtCtt9D8OwAbIVJuewQdfvQRaoFE9wd1TmCuRG7OgJ1bVXGHc7
2262 | z5WDL/WW36v2oi37CyVBak61+yPBA9C1qqGxzKQqZ0oPuocU9hpud0PIp8sDHkXR1HKkNlzjuUWA
2263 | a0enFUyzOWZA4yXGP+ZMI3Tdt2OuqU/SO4q64526cPE0A7ZyW2PMbWZiZ5HamIZ2RcCKLXhcDl2b
2264 | vXL+eccQoRzem80mekPDEiyiWK4GWqZmwxQOmPM0eIfgp1P9cqrBsewR2p/DPMtt+pfcYM+Ls2uh
2265 | hALufTAdmGl8B1H3VPd2af8fQAc4PgqjlIBL9cGQqNpXaAwe3LrtVn8AkZTUxg==
2266 | """)
2267 |
2268 | ##file activate.csh
2269 | ACTIVATE_CSH = convert("""
2270 | eJx9VG1P2zAQ/u5fcYQKNgTNPtN1WxlIQ4KCUEGaxuQ6yYVYSuzKdhqVX7+zk3bpy5YPUXL3PPfc
2271 | ne98DLNCWshliVDV1kGCUFvMoJGugMjq2qQIiVSxSJ1cCofD1BYRnOVGV0CfZ0N2DD91DalQSjsw
2272 | tQLpIJMGU1euvPe7QeJlkKzgWixlhnAt4aoUVsLnLBiy5NtbJWQ5THX1ZciYKKWwkOFaE04dUm6D
2273 | r/zh7pq/3D7Nnid3/HEy+wFHY/gEJydg0aFaQrBFgz1c5DG1IhTs+UZgsBC2GMFBlaeH+8dZXwcW
2274 | VPvCjXdlAvCfQsE7al0+07XjZvrSCUevR5dnkVeKlFYZmUztG4BdzL2u9KyLVabTU0bdfg7a0hgs
2275 | cSmUg6UwUiQl2iHrcbcVGNvPCiLOe7+cRwG13z9qRGgx2z6DHjfm/Op2yqeT+xvOLzs0PTKHDz2V
2276 | tkckFHoQfQRXoGJAj9el0FyJCmEMhzgMS4sB7KPOE2ExoLcSieYwDvR+cP8cg11gKkVJc2wRcm1g
2277 | QhYFlXiTaTfO2ki0fQoiFM4tLuO4aZrhOzqR4dIPcWx17hphMBY+Srwh7RTyN83XOWkcSPh1Pg/k
2278 | TXX/jbJTbMtUmcxZ+/bbqOsy82suFQg/BhdSOTRhMNBHlUarCpU7JzBhmkKmRejKOQzayQe6MWoa
2279 | n1wqWmuh6LZAaHxcdeqIlVLhIBJdO9/kbl0It2oEXQj+eGjJOuvOIR/YGRqvFhttUB2XTvLXYN2H
2280 | 37CBdbW2W7j2r2+VsCn0doVWcFG1/4y1VwBjfwAyoZhD
2281 | """)
2282 |
2283 | ##file activate.bat
2284 | ACTIVATE_BAT = convert("""
2285 | eJx9UdEKgjAUfW6wfxjiIH+hEDKUFHSKLCMI7kNOEkIf9P9pTJ3OLJ/03HPPPed4Es9XS9qqwqgT
2286 | PbGKKOdXL4aAFS7A4gvAwgijuiKlqOpGlATS2NeMLE+TjJM9RkQ+SmqAXLrBo1LLIeLdiWlD6jZt
2287 | r7VNubWkndkXaxg5GO3UaOOKS6drO3luDDiO5my3iA0YAKGzPRV1ack8cOdhysI0CYzIPzjSiH5X
2288 | 0QcvC8Lfaj0emsVKYF2rhL5L3fCkVjV76kShi59NHwDniAHzkgDgqBcwOgTMx+gDQQqXCw==
2289 | """)
2290 |
2291 | ##file deactivate.bat
2292 | DEACTIVATE_BAT = convert("""
2293 | eJxzSE3OyFfIT0vj4ipOLVEI8wwKCXX0iXf1C7Pl4spMU0hJTcvMS01RiPf3cYmHyQYE+fsGhCho
2294 | cCkAAUibEkTEVhWLMlUlLk6QGixStlyaeCyJDPHw9/Pw93VFsQguim4ZXAJoIUw5DhX47XUM8UCx
2295 | EchHtwsohN1bILUgw61c/Vy4AJYPYm4=
2296 | """)
2297 |
2298 | ##file activate.ps1
2299 | ACTIVATE_PS = convert("""
2300 | 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
2386 | QastYw==
2387 | """)
2388 |
2389 | ##file distutils-init.py
2390 | DISTUTILS_INIT = convert("""
2391 | eJytV1uL4zYUfvevOE0ottuMW9q3gVDa3aUMXXbLMlDKMBiNrSTqOJKRlMxkf33PkXyRbGe7Dw2E
2392 | UXTu37lpxLFV2oIyifAncxmOL0xLIfcG+gv80x9VW6maw7o/CANSWWBwFtqeWMPlGY6qPjV8A0bB
2393 | C4eKSTgZ5LRgFeyErMEeOBhbN+Ipgeizhjtnhkn7DdyjuNLPoCS0l/ayQTG0djwZC08cLXozeMss
2394 | aG5EzQ0IScpnWtHSTXuxByV/QCmxE7y+eS0uxWeoheaVVfqSJHiU7Mhhi6gULbOHorshkrEnKxpT
2395 | 0n3A8Y8SMpuwZx6aoix3ouFlmW8gHRSkeSJ2g7hU+kiHLDaQw3bmRDaTGfTnty7gPm0FHbIBg9U9
2396 | oh1kZzAFLaue2R6htPCtAda2nGlDSUJ4PZBgCJBGVcwKTAMz/vJiLD+Oin5Z5QlvDPdulC6EsiyE
2397 | NFzb7McNTKJzbJqzphx92VKRFY1idenzmq3K0emRcbWBD0ryqc4NZGmKOOOX9Pz5x+/l27tP797c
2398 | f/z0d+4NruGNai8uAM0bfsYaw8itFk8ny41jsfpyO+BWlpqfhcG4yxLdi/0tQqoT4a8Vby382mt8
2399 | p7XSo7aWGdPBc+b6utaBmCQ7rQKQoWtAuthQCiold2KfJIPTT8xwg9blPumc+YDZC/wYGdAyHpJk
2400 | vUbHbHWAp5No6pK/WhhLEWrFjUwtPEv1Agf8YmnsuXUQYkeZoHm8ogP16gt2uHoxcEMdf2C6pmbw
2401 | hUMsWGhanboh4IzzmsIpWs134jVPqD/c74bZHdY69UKKSn/+KfVhxLgUlToemayLMYQOqfEC61bh
2402 | cbhwaqoGUzIyZRFHPmau5juaWqwRn3mpWmoEA5nhzS5gog/5jbcFQqOZvmBasZtwYlG93k5GEiyw
2403 | buHhMWLjDarEGpMGB2LFs5nIJkhp/nUmZneFaRth++lieJtHepIvKgx6PJqIlD9X2j6pG1i9x3pZ
2404 | 5bHuCPFiirGHeO7McvoXkz786GaKVzC9DSpnOxJdc4xm6NSVq7lNEnKdVlnpu9BNYoKX2Iq3wvgh
2405 | gGEUM66kK6j4NiyoneuPLSwaCWDxczgaolEWpiMyDVDb7dNuLAbriL8ig8mmeju31oNvQdpnvEPC
2406 | 1vAXbWacGRVrGt/uXN/gU0CDDwgooKRrHfTBb1/s9lYZ8ZqOBU0yLvpuP6+K9hLFsvIjeNhBi0KL
2407 | MlOuWRn3FRwx5oHXjl0YImUx0+gLzjGchrgzca026ETmYJzPD+IpuKzNi8AFn048Thd63OdD86M6
2408 | 84zE8yQm0VqXdbbgvub2pKVnS76icBGdeTHHXTKspUmr4NYo/furFLKiMdQzFjHJNcdAnMhltBJK
2409 | 0/IKX3DVFqvPJ2dLE7bDBkH0l/PJ29074+F0CsGYOxsb7U3myTUncYfXqnLLfa6sJybX4g+hmcjO
2410 | kMRBfA1JellfRRKJcyRpxdS4rIl6FdmQCWjo/o9Qz7yKffoP4JHjOvABcRn4CZIT2RH4jnxmfpVG
2411 | qgLaAvQBNfuO6X0/Ux02nb4FKx3vgP+XnkX0QW9pLy/NsXgdN24dD3LxO2Nwil7Zlc1dqtP3d7/h
2412 | kzp1/+7hGBuY4pk0XD/0Ao/oTe/XGrfyM773aB7iUhgkpy+dwAMalxMP0DrBcsVw/6p25+/hobP9
2413 | GBknrWExDhLJ1bwt1NcCNblaFbMKCyvmX0PeRaQ=
2414 | """)
2415 |
2416 | ##file distutils.cfg
2417 | DISTUTILS_CFG = convert("""
2418 | eJxNj00KwkAMhfc9xYNuxe4Ft57AjYiUtDO1wXSmNJnK3N5pdSEEAu8nH6lxHVlRhtDHMPATA4uH
2419 | xJ4EFmGbvfJiicSHFRzUSISMY6hq3GLCRLnIvSTnEefN0FIjw5tF0Hkk9Q5dRunBsVoyFi24aaLg
2420 | 9FDOlL0FPGluf4QjcInLlxd6f6rqkgPu/5nHLg0cXCscXoozRrP51DRT3j9QNl99AP53T2Q=
2421 | """)
2422 |
2423 | ##file activate_this.py
2424 | ACTIVATE_THIS = convert("""
2425 | eJyNU01v2zAMvetXEB4K21jmDOstQA4dMGCHbeihlyEIDMWmG62yJEiKE//7kXKdpN2KzYBt8euR
2426 | fKSyLPs8wiEo8wh4wqZTGou4V6Hm0wJa1cSiTkJdr8+GsoTRHuCotBayiWqQEYGtMCgfD1KjGYBe
2427 | 5a3p0cRKiAe2NtLADikftnDco0ko/SFEVgEZ8aRC5GLux7i3BpSJ6J1H+i7A2CjiHq9z7JRZuuQq
2428 | siwTIvpxJYCeuWaBpwZdhB+yxy/eWz+ZvVSU8C4E9FFZkyxFsvCT/ZzL8gcz9aXVE14Yyp2M+2W0
2429 | y7n5mp0qN+avKXvbsyyzUqjeWR8hjGE+2iCE1W1tQ82hsCZN9UzlJr+/e/iab8WfqsmPI6pWeUPd
2430 | FrMsd4H/55poeO9n54COhUs+sZNEzNtg/wanpjpuqHJaxs76HtZryI/K3H7KJ/KDIhqcbJ7kI4ar
2431 | XL+sMgXnX0D+Te2Iy5xdP8yueSlQB/x/ED2BTAtyE3K4SYUN6AMNfbO63f4lBW3bUJPbTL+mjSxS
2432 | PyRfJkZRgj+VbFv+EzHFi5pKwUEepa4JslMnwkowSRCXI+m5XvEOvtuBrxHdhLalG0JofYBok6qj
2433 | YdN2dEngUlbC4PG60M1WEN0piu7Nq7on0mgyyUw3iV1etLo6r/81biWdQ9MWHFaePWZYaq+nmp+t
2434 | s3az+sj7eA0jfgPfeoN1
2435 | """)
2436 |
2437 | MH_MAGIC = 0xfeedface
2438 | MH_CIGAM = 0xcefaedfe
2439 | MH_MAGIC_64 = 0xfeedfacf
2440 | MH_CIGAM_64 = 0xcffaedfe
2441 | FAT_MAGIC = 0xcafebabe
2442 | BIG_ENDIAN = '>'
2443 | LITTLE_ENDIAN = '<'
2444 | LC_LOAD_DYLIB = 0xc
2445 | maxint = majver == 3 and getattr(sys, 'maxsize') or getattr(sys, 'maxint')
2446 |
2447 |
2448 | class fileview(object):
2449 | """
2450 | A proxy for file-like objects that exposes a given view of a file.
2451 | Modified from macholib.
2452 | """
2453 |
2454 | def __init__(self, fileobj, start=0, size=maxint):
2455 | if isinstance(fileobj, fileview):
2456 | self._fileobj = fileobj._fileobj
2457 | else:
2458 | self._fileobj = fileobj
2459 | self._start = start
2460 | self._end = start + size
2461 | self._pos = 0
2462 |
2463 | def __repr__(self):
2464 | return '' % (
2465 | self._start, self._end, self._fileobj)
2466 |
2467 | def tell(self):
2468 | return self._pos
2469 |
2470 | def _checkwindow(self, seekto, op):
2471 | if not (self._start <= seekto <= self._end):
2472 | raise IOError("%s to offset %d is outside window [%d, %d]" % (
2473 | op, seekto, self._start, self._end))
2474 |
2475 | def seek(self, offset, whence=0):
2476 | seekto = offset
2477 | if whence == os.SEEK_SET:
2478 | seekto += self._start
2479 | elif whence == os.SEEK_CUR:
2480 | seekto += self._start + self._pos
2481 | elif whence == os.SEEK_END:
2482 | seekto += self._end
2483 | else:
2484 | raise IOError("Invalid whence argument to seek: %r" % (whence,))
2485 | self._checkwindow(seekto, 'seek')
2486 | self._fileobj.seek(seekto)
2487 | self._pos = seekto - self._start
2488 |
2489 | def write(self, bytes):
2490 | here = self._start + self._pos
2491 | self._checkwindow(here, 'write')
2492 | self._checkwindow(here + len(bytes), 'write')
2493 | self._fileobj.seek(here, os.SEEK_SET)
2494 | self._fileobj.write(bytes)
2495 | self._pos += len(bytes)
2496 |
2497 | def read(self, size=maxint):
2498 | assert size >= 0
2499 | here = self._start + self._pos
2500 | self._checkwindow(here, 'read')
2501 | size = min(size, self._end - here)
2502 | self._fileobj.seek(here, os.SEEK_SET)
2503 | bytes = self._fileobj.read(size)
2504 | self._pos += len(bytes)
2505 | return bytes
2506 |
2507 |
2508 | def read_data(file, endian, num=1):
2509 | """
2510 | Read a given number of 32-bits unsigned integers from the given file
2511 | with the given endianness.
2512 | """
2513 | res = struct.unpack(endian + 'L' * num, file.read(num * 4))
2514 | if len(res) == 1:
2515 | return res[0]
2516 | return res
2517 |
2518 |
2519 | def mach_o_change(path, what, value):
2520 | """
2521 | Replace a given name (what) in any LC_LOAD_DYLIB command found in
2522 | the given binary with a new name (value), provided it's shorter.
2523 | """
2524 |
2525 | def do_macho(file, bits, endian):
2526 | # Read Mach-O header (the magic number is assumed read by the caller)
2527 | cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = read_data(file, endian, 6)
2528 | # 64-bits header has one more field.
2529 | if bits == 64:
2530 | read_data(file, endian)
2531 | # The header is followed by ncmds commands
2532 | for n in range(ncmds):
2533 | where = file.tell()
2534 | # Read command header
2535 | cmd, cmdsize = read_data(file, endian, 2)
2536 | if cmd == LC_LOAD_DYLIB:
2537 | # The first data field in LC_LOAD_DYLIB commands is the
2538 | # offset of the name, starting from the beginning of the
2539 | # command.
2540 | name_offset = read_data(file, endian)
2541 | file.seek(where + name_offset, os.SEEK_SET)
2542 | # Read the NUL terminated string
2543 | load = file.read(cmdsize - name_offset).decode()
2544 | load = load[:load.index('\0')]
2545 | # If the string is what is being replaced, overwrite it.
2546 | if load == what:
2547 | file.seek(where + name_offset, os.SEEK_SET)
2548 | file.write(value.encode() + '\0'.encode())
2549 | # Seek to the next command
2550 | file.seek(where + cmdsize, os.SEEK_SET)
2551 |
2552 | def do_file(file, offset=0, size=maxint):
2553 | file = fileview(file, offset, size)
2554 | # Read magic number
2555 | magic = read_data(file, BIG_ENDIAN)
2556 | if magic == FAT_MAGIC:
2557 | # Fat binaries contain nfat_arch Mach-O binaries
2558 | nfat_arch = read_data(file, BIG_ENDIAN)
2559 | for n in range(nfat_arch):
2560 | # Read arch header
2561 | cputype, cpusubtype, offset, size, align = read_data(file, BIG_ENDIAN, 5)
2562 | do_file(file, offset, size)
2563 | elif magic == MH_MAGIC:
2564 | do_macho(file, 32, BIG_ENDIAN)
2565 | elif magic == MH_CIGAM:
2566 | do_macho(file, 32, LITTLE_ENDIAN)
2567 | elif magic == MH_MAGIC_64:
2568 | do_macho(file, 64, BIG_ENDIAN)
2569 | elif magic == MH_CIGAM_64:
2570 | do_macho(file, 64, LITTLE_ENDIAN)
2571 |
2572 | assert(len(what) >= len(value))
2573 | do_file(open(path, 'r+b'))
2574 |
2575 |
2576 | if __name__ == '__main__':
2577 | main()
2578 |
2579 | ## TODO:
2580 | ## Copy python.exe.manifest
2581 | ## Monkeypatch distutils.sysconfig
2582 |
--------------------------------------------------------------------------------
/entity/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var util = require('util'),
3 | yeoman = require('yeoman-generator'),
4 | fs = require('fs'),
5 | _ = require('lodash'),
6 | _s = require('underscore.string'),
7 | pluralize = require('pluralize');
8 |
9 | var EntityGenerator = module.exports = function EntityGenerator(args, options, config) {
10 | // By calling `NamedBase` here, we get the argument to the subgenerator call
11 | // as `this.name`.
12 | yeoman.generators.NamedBase.apply(this, arguments);
13 |
14 | console.log('You called the entity subgenerator with the argument ' + this.name + '.');
15 |
16 | //this.on('end', function () {
17 | // return this.spawnCommand('rake', ['upgrade']);
18 | //});
19 |
20 | fs.readFile('generator.json', 'utf8', function (err, data) {
21 | if (err) {
22 | console.log('Error: ' + err);
23 | return;
24 | }
25 | this.generatorConfig = JSON.parse(data);
26 | }.bind(this));
27 | };
28 |
29 | util.inherits(EntityGenerator, yeoman.generators.NamedBase);
30 |
31 | EntityGenerator.prototype.askFor = function askFor() {
32 | var cb = this.async();
33 |
34 | console.log('\nPlease specify an attribute:');
35 |
36 | var prompts = [{
37 | type: 'input',
38 | name: 'attrName',
39 | message: 'What is the name of the attribute?',
40 | default: 'myattr'
41 | },
42 | {
43 | type: 'list',
44 | name: 'attrType',
45 | message: 'What is the type of the attribute?',
46 | choices: ['String', 'Integer', 'Float', 'Boolean', 'Date', 'Enum'],
47 | default: 'String'
48 | },
49 | {
50 | when: function (props) { return (/String/).test(props.attrType); },
51 | type: 'input',
52 | name: 'minLength',
53 | message: 'Enter the minimum length for the String attribute, or hit enter:',
54 | validate: function (input) {
55 | if (input && isNaN(input)) {
56 | return "Please enter a number.";
57 | }
58 | return true;
59 | }
60 | },
61 | {
62 | when: function (props) { return (/String/).test(props.attrType); },
63 | type: 'input',
64 | name: 'maxLength',
65 | message: 'Enter the maximum length for the String attribute, or hit enter:',
66 | validate: function (input) {
67 | if (input && isNaN(input)) {
68 | return "Please enter a number.";
69 | }
70 | return true;
71 | }
72 | },
73 | {
74 | when: function (props) { return (/Integer|Float/).test(props.attrType); },
75 | type: 'input',
76 | name: 'min',
77 | message: 'Enter the minimum value for the numeric attribute, or hit enter:',
78 | validate: function (input) {
79 | if (input && isNaN(input)) {
80 | return "Please enter a number.";
81 | }
82 | return true;
83 | }
84 | },
85 | {
86 | when: function (props) { return (/Integer|Float/).test(props.attrType); },
87 | type: 'input',
88 | name: 'max',
89 | message: 'Enter the maximum value for the numeric attribute, or hit enter:',
90 | validate: function (input) {
91 | if (input && isNaN(input)) {
92 | return "Please enter a number.";
93 | }
94 | return true;
95 | }
96 | },
97 | {
98 | when: function (props) { return (/Date/).test(props.attrType); },
99 | type: 'list',
100 | name: 'dateConstraint',
101 | message: 'Constrain the date as follows:',
102 | choices: ['None', 'Past dates only', 'Future dates only'],
103 | filter: function (input) {
104 | if (/Past/.test(input)) return 'Past';
105 | if (/Future/.test(input)) return 'Future';
106 | return '';
107 | },
108 | default: 'None'
109 | },
110 | {
111 | when: function (props) { return (/Enum/).test(props.attrType); },
112 | type: 'input',
113 | name: 'enumValues',
114 | message: 'Enter an enumeration of values, separated by commas'
115 | },
116 | {
117 | type: 'confirm',
118 | name: 'required',
119 | message: 'Is the attribute required to have a value?',
120 | default: true
121 | },
122 | {
123 | type: 'confirm',
124 | name: 'again',
125 | message: 'Would you like to enter another attribute or reenter a previous attribute?',
126 | default: true
127 | }];
128 |
129 | this.prompt(prompts, function (props) {
130 | this.attrs = this.attrs || [];
131 | var attrType = props.attrType;
132 | this.attrs = _.reject(this.attrs, function (attr) { return attr.attrName === props.attrName; });
133 | this.attrs.push({
134 | attrName: props.attrName,
135 | attrType: attrType,
136 | minLength: props.minLength,
137 | maxLength: props.maxLength,
138 | min: props.min,
139 | max: props.max,
140 | dateConstraint: props.dateConstraint,
141 | enumValues: props.enumValues ? props.enumValues.split(',') : [],
142 | required: props.required
143 | });
144 |
145 | if (props.again) {
146 | this.askFor();
147 | } else {
148 | cb();
149 | }
150 | }.bind(this));
151 | };
152 |
153 | EntityGenerator.prototype.files = function files() {
154 |
155 | this.baseName = this.generatorConfig.baseName;
156 | this.packageName = this.generatorConfig.packageName;
157 | this.entities = this.generatorConfig.entities;
158 | this.entities = _.reject(this.entities, function (entity) { return entity.name === this.name; }.bind(this));
159 | this.entities.push({ name: this.name, attrs: this.attrs});
160 | this.pluralize = pluralize;
161 | this.generatorConfig.entities = this.entities;
162 | this.generatorConfigStr = JSON.stringify(this.generatorConfig, null, '\t');
163 |
164 | var appDir = 'app/';
165 | this.template('_generator.json', 'generator.json');
166 | this.template('../../app/templates/app/___init__.py', appDir + '__init__.py');
167 | this.template('app/models/_entity.py', appDir + 'models/' + this.name + '.py');
168 | this.template('app/routes/_entities.py', appDir + 'routes/' + pluralize(this.name) + '.py');
169 |
170 | var staticDir = appDir + 'static/';
171 | var staticCssDir = staticDir + 'css/';
172 | var staticJsDir = staticDir + 'js/';
173 | var staticViewDir = staticDir + 'views/';
174 | var staticEntityJsDir = staticJsDir + this.name + '/';
175 | var staticEntityViewDir = staticViewDir + this.name + '/';
176 | this.mkdir(staticEntityJsDir);
177 | this.mkdir(staticEntityViewDir);
178 | this.template('../../app/templates/app/static/_index.html', staticDir + 'index.html');
179 | this.template('app/static/js/entity/_entity-controller.js', staticEntityJsDir + this.name + '-controller.js');
180 | this.template('app/static/js/entity/_entity-router.js', staticEntityJsDir + this.name + '-router.js');
181 | this.template('app/static/js/entity/_entity-service.js', staticEntityJsDir + this.name + '-service.js');
182 | this.template('app/static/views/entity/_entities.html', staticEntityViewDir + pluralize(this.name) + '.html');
183 | };
184 |
--------------------------------------------------------------------------------
/entity/templates/_generator.json:
--------------------------------------------------------------------------------
1 | <%= generatorConfigStr %>
2 |
--------------------------------------------------------------------------------
/entity/templates/app/models/_entity.py:
--------------------------------------------------------------------------------
1 | from app import db
2 |
3 | class <%= _.capitalize(name) %>(db.Model):
4 | id = db.Column(db.Integer, primary_key = True)
5 | <% _.each(attrs, function (attr) { %>
6 | <%= attr.attrName %> = db.Column(db.<% if (attr.attrType == 'Enum') { %>Enum(<% var delim = ''; _.each(attr.enumValues, function (value) { %><%= delim %>'<%= value %>'<% delim = ', '; }) %>)<% } else { %><%= attr.attrType %><% }; %>)
7 | <% }); %>
8 |
9 | def to_dict(self):
10 | return dict(<% _.each(attrs, function (attr) { %>
11 | <%= attr.attrName %> = self.<%= attr.attrName %><% if (attr.attrType == 'Date') { %>.isoformat()<% }; %>,<% }); %>
12 | id = self.id
13 | )
14 |
15 | def __repr__(self):
16 | return '<<%= _.capitalize(name) %> %r>' % (self.id)
17 |
--------------------------------------------------------------------------------
/entity/templates/app/routes/_entities.py:
--------------------------------------------------------------------------------
1 | from app import app, db
2 | from app.models import <%= name %>
3 | from flask import abort, jsonify, request
4 | import datetime
5 | import json
6 |
7 | @app.route('/<%= baseName %>/<%= pluralize(name) %>', methods = ['GET'])
8 | def get_all_<%= pluralize(name) %>():
9 | entities = <%= name %>.<%= _.capitalize(name) %>.query.all()
10 | return json.dumps([entity.to_dict() for entity in entities])
11 |
12 | @app.route('/<%= baseName %>/<%= pluralize(name) %>/', methods = ['GET'])
13 | def get_<%= name %>(id):
14 | entity = <%= name %>.<%= _.capitalize(name) %>.query.get(id)
15 | if not entity:
16 | abort(404)
17 | return jsonify(entity.to_dict())
18 |
19 | @app.route('/<%= baseName %>/<%= pluralize(name) %>', methods = ['POST'])
20 | def create_<%= name %>():
21 | entity = <%= name %>.<%= _.capitalize(name) %>(<% var delim = ''; _.each(attrs, function (attr) { %>
22 | <%= delim %><%= attr.attrName %> = <% if (attr.attrType == 'Date') { %>datetime.datetime.strptime(request.json['<%= attr.attrName %>'], "%Y-%m-%d").date()<% } else { %>request.json['<%= attr.attrName %>']<% } %><% delim = ', '; }); %>
23 | )
24 | db.session.add(entity)
25 | db.session.commit()
26 | return jsonify(entity.to_dict()), 201
27 |
28 | @app.route('/<%= baseName %>/<%= pluralize(name) %>/', methods = ['PUT'])
29 | def update_<%= name %>(id):
30 | entity = <%= name %>.<%= _.capitalize(name) %>.query.get(id)
31 | if not entity:
32 | abort(404)
33 | entity = <%= name %>.<%= _.capitalize(name) %>(<% _.each(attrs, function (attr) { %>
34 | <%= attr.attrName %> = <% if (attr.attrType == 'Date') { %>datetime.datetime.strptime(request.json['<%= attr.attrName %>'], "%Y-%m-%d").date(),<% } else { %>request.json['<%= attr.attrName %>'],<% }}); %>
35 | id = id
36 | )
37 | db.session.merge(entity)
38 | db.session.commit()
39 | return jsonify(entity.to_dict()), 200
40 |
41 | @app.route('/<%= baseName %>/<%= pluralize(name) %>/', methods = ['DELETE'])
42 | def delete_<%= name %>(id):
43 | entity = <%= name %>.<%= _.capitalize(name) %>.query.get(id)
44 | if not entity:
45 | abort(404)
46 | db.session.delete(entity)
47 | db.session.commit()
48 | return '', 204
49 |
--------------------------------------------------------------------------------
/entity/templates/app/static/js/entity/_entity-controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= baseName %>')
4 | .controller('<%= _.capitalize(name) %>Controller', ['$scope', '$modal', 'resolved<%= _.capitalize(name) %>', '<%= _.capitalize(name) %>',
5 | function ($scope, $modal, resolved<%= _.capitalize(name) %>, <%= _.capitalize(name) %>) {
6 |
7 | $scope.<%= pluralize(name) %> = resolved<%= _.capitalize(name) %>;
8 |
9 | $scope.create = function () {
10 | $scope.clear();
11 | $scope.open();
12 | };
13 |
14 | $scope.update = function (id) {
15 | $scope.<%= name %> = <%= _.capitalize(name) %>.get({id: id});
16 | $scope.open(id);
17 | };
18 |
19 | $scope.delete = function (id) {
20 | <%= _.capitalize(name) %>.delete({id: id},
21 | function () {
22 | $scope.<%= pluralize(name) %> = <%= _.capitalize(name) %>.query();
23 | });
24 | };
25 |
26 | $scope.save = function (id) {
27 | if (id) {
28 | <%= _.capitalize(name) %>.update({id: id}, $scope.<%= name %>,
29 | function () {
30 | $scope.<%= pluralize(name) %> = <%= _.capitalize(name) %>.query();
31 | $scope.clear();
32 | });
33 | } else {
34 | <%= _.capitalize(name) %>.save($scope.<%= name %>,
35 | function () {
36 | $scope.<%= pluralize(name) %> = <%= _.capitalize(name) %>.query();
37 | $scope.clear();
38 | });
39 | }
40 | };
41 |
42 | $scope.clear = function () {
43 | $scope.<%= name %> = {
44 | <% _.each(attrs, function (attr) { %>
45 | "<%= attr.attrName %>": "",
46 | <% }); %>
47 | "id": ""
48 | };
49 | };
50 |
51 | $scope.open = function (id) {
52 | var <%= name %>Save = $modal.open({
53 | templateUrl: '<%= name %>-save.html',
54 | controller: '<%= _.capitalize(name) %>SaveController',
55 | resolve: {
56 | <%= name %>: function () {
57 | return $scope.<%= name %>;
58 | }
59 | }
60 | });
61 |
62 | <%= name %>Save.result.then(function (entity) {
63 | $scope.<%= name %> = entity;
64 | $scope.save(id);
65 | });
66 | };
67 | }])
68 | .controller('<%= _.capitalize(name) %>SaveController', ['$scope', '$modalInstance', '<%= name %>',
69 | function ($scope, $modalInstance, <%= name %>) {
70 | $scope.<%= name %> = <%= name %>;
71 |
72 | <% _.each(attrs, function (attr) { if (attr.attrType === 'Date') { %>
73 | $scope.<%= attr.attrName %>DateOptions = {
74 | dateFormat: 'yy-mm-dd',
75 | <% if (attr.dateConstraint === 'Past') { %>maxDate: -1<% } %>
76 | <% if (attr.dateConstraint === 'Future') { %>minDate: 1<% } %>
77 | };<% }}); %>
78 |
79 | $scope.ok = function () {
80 | $modalInstance.close($scope.<%= name %>);
81 | };
82 |
83 | $scope.cancel = function () {
84 | $modalInstance.dismiss('cancel');
85 | };
86 | }]);
87 |
--------------------------------------------------------------------------------
/entity/templates/app/static/js/entity/_entity-router.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= baseName %>')
4 | .config(['$routeProvider', function ($routeProvider) {
5 | $routeProvider
6 | .when('/<%= pluralize(name) %>', {
7 | templateUrl: 'views/<%= name %>/<%= pluralize(name) %>.html',
8 | controller: '<%= _.capitalize(name) %>Controller',
9 | resolve:{
10 | resolved<%= _.capitalize(name) %>: ['<%= _.capitalize(name) %>', function (<%= _.capitalize(name) %>) {
11 | return <%= _.capitalize(name) %>.query();
12 | }]
13 | }
14 | })
15 | }]);
16 |
--------------------------------------------------------------------------------
/entity/templates/app/static/js/entity/_entity-service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('<%= baseName %>')
4 | .factory('<%= _.capitalize(name) %>', ['$resource', function ($resource) {
5 | return $resource('<%= baseName %>/<%= pluralize(name) %>/:id', {}, {
6 | 'query': { method: 'GET', isArray: true},
7 | 'get': { method: 'GET'},
8 | 'update': { method: 'PUT'}
9 | });
10 | }]);
11 |
--------------------------------------------------------------------------------
/entity/templates/app/static/views/entity/_entities.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
<%= _.capitalize(pluralize(name)) %>
4 |
5 |
64 |
65 |
68 |
69 |
70 |
71 |
72 |
73 | ID |
74 | <% _.each(attrs, function (attr) { %>
75 | <%= attr.attrName %> |
76 | <% }); %>
77 |
78 |
79 |
80 |
81 | {{<%= name %>.id}} |
82 | <% _.each(attrs, function (attr) { %>
83 | {{<%= name %>.<%= attr.attrName %> <% if (attr.attrType === 'Date') { %> | date:'yyyy-MM-dd'<% } %>}} |
84 | <% }); %>
85 |
86 |
91 |
96 | |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-angular-flask",
3 | "version": "0.1.12",
4 | "description": "A Yeoman generator for AngularJS + Flask",
5 | "keywords": [
6 | "yeoman-generator",
7 | "Python",
8 | "Flask",
9 | "SQLAlchemy",
10 | "AngularJS",
11 | "Bootstrap"
12 | ],
13 | "homepage": "https://github.com/rayokota/generator-angular-flask",
14 | "bugs": "https://github.com/rayokota/generator-angular-flask/issues",
15 | "author": {
16 | "name": "Robert Yokota",
17 | "email": "",
18 | "url": "https://github.com/rayokota"
19 | },
20 | "main": "app/index.js",
21 | "repository": {
22 | "type": "git",
23 | "url": "git://github.com/rayokota/generator-angular-flask.git"
24 | },
25 | "scripts": {
26 | "test": "mocha"
27 | },
28 | "dependencies": {
29 | "yeoman-generator": "~0.14.0",
30 | "URIjs": "~1.11.2",
31 | "lodash": "~2.4.1",
32 | "underscore.string": "~2.3.3",
33 | "pluralize": "~0.0.6",
34 | "asciify": "~1.3.3"
35 | },
36 | "devDependencies": {
37 | "mocha": "~1.14.0"
38 | },
39 | "peerDependencies": {
40 | "yo": ">=1.0.0"
41 | },
42 | "engines": {
43 | "node": ">=0.8.0",
44 | "npm": ">=1.2.10"
45 | },
46 | "licenses": [
47 | {
48 | "type": "MIT"
49 | }
50 | ]
51 | }
52 |
--------------------------------------------------------------------------------
/test/test-creation.js:
--------------------------------------------------------------------------------
1 | /*global describe, beforeEach, it*/
2 | 'use strict';
3 |
4 | var path = require('path');
5 | var helpers = require('yeoman-generator').test;
6 |
7 |
8 | describe('angular-flask generator', function () {
9 | beforeEach(function (done) {
10 | helpers.testDirectory(path.join(__dirname, 'temp'), function (err) {
11 | if (err) {
12 | return done(err);
13 | }
14 |
15 | this.app = helpers.createGenerator('angular-flask:app', [
16 | '../../app'
17 | ]);
18 | done();
19 | }.bind(this));
20 | });
21 |
22 | it('creates expected files', function (done) {
23 | var expected = [
24 | // add files you expect to exist here.
25 | '.jshintrc',
26 | '.editorconfig'
27 | ];
28 |
29 | helpers.mockPrompt(this.app, {
30 | 'someOption': true
31 | });
32 | this.app.options['skip-install'] = true;
33 | this.app.run({}, function () {
34 | helpers.assertFiles(expected);
35 | done();
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/test-load.js:
--------------------------------------------------------------------------------
1 | /*global describe, beforeEach, it*/
2 | 'use strict';
3 |
4 | var assert = require('assert');
5 |
6 | describe('angular-flask generator', function () {
7 | it('can be imported without blowing up', function () {
8 | var app = require('../app');
9 | assert(app !== undefined);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------