From 74cf5ad156562d71ea58d57ecda4e82e531e4006 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Tue, 20 Sep 2016 19:16:03 +0800 Subject: [PATCH 01/77] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9bc4039..e32f0be 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,17 @@ Run your python source file (.py) using `F5` or `F6`! 1. Open a .py file. 2. Hit `F5` or `F6` to run. + - **Save the file in current editor immediately without a notification, be aware.** # Features - Using `python.exe` - - almost the same console with python IDLE + - almost the same console with python IDLE, which provides syntax error and runtime error messages. - CodeBlocks debug console style - shows return value and execution time - + - It is a rough time based on real time rather than CPU kernel time or CPU user time +- New an issue if you have any idea of new features. ![A screenshot of your package](https://f.cloud.github.com/assets/69169/2290250/c35d867a-a017-11e3-86be-cd7c5bf3ff9b.gif) + +This is a package for Atom From e0131b70d4b79e43c74d10585a00610e59990786 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Tue, 20 Sep 2016 19:20:38 +0800 Subject: [PATCH 02/77] Force set cwd --- CHANGELOG.md | 3 +++ lib/atom-python-run.js | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1674837..73cc42b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.5.2 +* force set cwd + ## 0.5.0 * thanks to Frezzu for fixing the missing dll bug diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index 9badc0f..69b10de 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -27,7 +27,8 @@ function run() { var child = child_process.spawn("cmd", [ "/c", "start", __dirname + "/../bin/cp.exe", "python.exe", "\"" + file + "\"" ], { - detach: true + cwd: info.dir, + detached: true }); child.unref(); } From ad9cc757be16b0a98b9f0761a16cccf190235636 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Tue, 20 Sep 2016 19:29:56 +0800 Subject: [PATCH 03/77] Prepare 0.5.2 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cddf6a1..56616dd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.5.1", + "version": "0.5.2", "description": "Run a python source file.", "keywords": [ "python" From c0d58781860aa8c11b7551610b639198ff66c1b3 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Fri, 23 Sep 2016 12:40:14 +0800 Subject: [PATCH 04/77] Add an option of disabling notifications --- CHANGELOG.md | 3 +++ README.md | 8 +++++--- lib/atom-python-run.js | 24 ++++++++++++++++++------ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73cc42b..d70ca18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.6.0 +* create an option to disable notifications on success, false by default + ## 0.5.2 * force set cwd diff --git a/README.md b/README.md index e32f0be..ec1f615 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Run your python source file (.py) using `F5` or `F6`! +![](https://cloud.githubusercontent.com/assets/2712675/18710388/9a665ed8-8037-11e6-803a-35e4555e89d0.jpg) + # Prerequisite - Windows @@ -11,14 +13,14 @@ Run your python source file (.py) using `F5` or `F6`! 1. Open a .py file. 2. Hit `F5` or `F6` to run. - - **Save the file in current editor immediately without a notification, be aware.** + - **It will save the file in current editor immediately without a notification, be aware.** # Features - Using `python.exe` - - almost the same console with python IDLE, which provides syntax error and runtime error messages. + - Almost the same console with python IDLE, which provides syntax error and runtime error messages. - CodeBlocks debug console style - - shows return value and execution time + - Shows return value and execution time - It is a rough time based on real time rather than CPU kernel time or CPU user time - New an issue if you have any idea of new features. diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index 69b10de..0319fbd 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -1,12 +1,20 @@ +/* global atom */ "use strict"; -var child_process = require("child_process"); -var path = require("path"); -var process = require("process"); +const path = require("path"); +const child_process = require("child_process"); module.exports = { activate: () => { atom.commands.add("atom-text-editor", "Python run: run", run); + }, + config: { + disable_notifications: { + title: "Disable notifications on success", + description: "Disable saving and running notifications", + type: "boolean", + default: false + } } }; @@ -15,15 +23,19 @@ function run() { if (!editor) { return; } - var file = atom.workspace.getActiveTextEditor().buffer.file.path; + var file = editor.buffer.file.path; var info = path.parse(file); - atom.notifications.add("info", "Saving " + info.base + " ..."); + if (!atom.config.get("atom-python-run.disable_notifications")) { + atom.notifications.add("info", "Saving " + info.base + " ..."); + } editor.save(); if (info.ext != ".py") { atom.notifications.add("warning", info.base + " is not a .py file, exit."); return; } - atom.notifications.add("info", "Running " + info.base + " ..."); + if (!atom.config.get("atom-python-run.disable_notifications")) { + atom.notifications.add("info", "Running " + info.base + " ..."); + } var child = child_process.spawn("cmd", [ "/c", "start", __dirname + "/../bin/cp.exe", "python.exe", "\"" + file + "\"" ], { From fa80d8acb719dc3fdbb8c3a5a300711f539fd0e3 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Fri, 23 Sep 2016 13:01:43 +0800 Subject: [PATCH 05/77] Prepare 0.6.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 56616dd..4bcba40 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.5.2", + "version": "0.6.0", "description": "Run a python source file.", "keywords": [ "python" From 440545f332650819d00fb0ccd5a9ee8612a4a294 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Sun, 25 Sep 2016 13:14:49 +0800 Subject: [PATCH 06/77] Create config: disable notifications of failure --- CHANGELOG.md | 5 ++++- lib/atom-python-run.js | 14 +++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d70ca18..ee024fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ +## 0.6.1 +* create an option to disable notifications of failure, false by default + ## 0.6.0 -* create an option to disable notifications on success, false by default +* create an option to disable notifications of success, false by default ## 0.5.2 * force set cwd diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index 0319fbd..0a3d1d7 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -10,8 +10,14 @@ module.exports = { }, config: { disable_notifications: { - title: "Disable notifications on success", - description: "Disable saving and running notifications", + title: "Disable notifications of success", + description: "Disable notifications of saving and running", + type: "boolean", + default: false + }, + disable_notifications_on_fail: { + title: "Disable notifications of failure", + description: "Disable notifications of extension name does not match", type: "boolean", default: false } @@ -30,7 +36,9 @@ function run() { } editor.save(); if (info.ext != ".py") { - atom.notifications.add("warning", info.base + " is not a .py file, exit."); + if (!atom.config.get("atom-python-run.disable_notifications_on_fail")) { + atom.notifications.add("warning", info.base + " is not a .py file, exit."); + } return; } if (!atom.config.get("atom-python-run.disable_notifications")) { From 97c7bac1b7c4548de4f1e75f2f18767cdbfef52d Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Sun, 25 Sep 2016 13:16:59 +0800 Subject: [PATCH 07/77] Prepare 0.6.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4bcba40..96dd342 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.6.0", + "version": "0.6.1", "description": "Run a python source file.", "keywords": [ "python" From 2443798fd18f0e0afbd3e8342b5e0d176ac141e7 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Sun, 25 Sep 2016 13:42:56 +0800 Subject: [PATCH 08/77] Fix bug: Uncaught TypeError: Cannot read property 'path' of null --- CHANGELOG.md | 3 +++ README.md | 2 +- lib/atom-python-run.js | 14 ++++++++++---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee024fe..1f1b18e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.6.2 +* Fix bug: Uncaught TypeError: Cannot read property 'path' of null + ## 0.6.1 * create an option to disable notifications of failure, false by default diff --git a/README.md b/README.md index ec1f615..8af0f1d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Run your python source file (.py) using `F5` or `F6`! 1. Open a .py file. 2. Hit `F5` or `F6` to run. - - **It will save the file in current editor immediately without a notification, be aware.** + - **It will save the file in current editor immediately without a confirmation, be aware.** # Features diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index 0a3d1d7..e284e84 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -29,12 +29,18 @@ function run() { if (!editor) { return; } - var file = editor.buffer.file.path; - var info = path.parse(file); + var file = editor.buffer.file; + if (!file) { + //if (!atom.config.get("atom-python-run.disable_notifications_on_fail")) { + atom.notifications.add("warning", "You have to create the file first."); + //} + return; + } if (!atom.config.get("atom-python-run.disable_notifications")) { - atom.notifications.add("info", "Saving " + info.base + " ..."); + atom.notifications.add("info", "Saving..."); } editor.save(); + var info = path.parse(file.path); if (info.ext != ".py") { if (!atom.config.get("atom-python-run.disable_notifications_on_fail")) { atom.notifications.add("warning", info.base + " is not a .py file, exit."); @@ -45,7 +51,7 @@ function run() { atom.notifications.add("info", "Running " + info.base + " ..."); } var child = child_process.spawn("cmd", [ - "/c", "start", __dirname + "/../bin/cp.exe", "python.exe", "\"" + file + "\"" + "/c", "start", __dirname + "/../bin/cp.exe", "python.exe", "\"" + file.path + "\"" ], { cwd: info.dir, detached: true From 49a4c0c092a4f5d530e7e0db63ea6b147441d892 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Sun, 25 Sep 2016 13:44:00 +0800 Subject: [PATCH 09/77] Prepare 0.6.2 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 96dd342..9af69b3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.6.1", + "version": "0.6.2", "description": "Run a python source file.", "keywords": [ "python" From 778a7ccd9d5a2bf342f7102106cb3a1d72478920 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Fri, 30 Sep 2016 17:35:32 +0800 Subject: [PATCH 10/77] Fix bug: does not dispose when deactivate --- lib/atom-python-run.js | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index e284e84..1fba0e8 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -1,27 +1,35 @@ /* global atom */ "use strict"; +const CompositeDisposable = require("atom").CompositeDisposable; const path = require("path"); const child_process = require("child_process"); module.exports = { activate: () => { - atom.commands.add("atom-text-editor", "Python run: run", run); + this.subscriptions = new CompositeDisposable(); + this.subscriptions.add( + atom.commands.add("atom-text-editor", "Python run: run", run) + ); + }, + deactivate: () => { + this.subscriptions.dispose(); }, config: { disable_notifications: { - title: "Disable notifications of success", - description: "Disable notifications of saving and running", - type: "boolean", - default: false + title: "Disable notifications of success", + description: "Disable notifications of saving and running", + type: "boolean", + default: false }, disable_notifications_on_fail: { - title: "Disable notifications of failure", - description: "Disable notifications of extension name does not match", - type: "boolean", - default: false + title: "Disable notifications of failure", + description: "Disable notifications of extension name does not match", + type: "boolean", + default: false } - } + }, + subscriptions: null }; function run() { @@ -32,7 +40,7 @@ function run() { var file = editor.buffer.file; if (!file) { //if (!atom.config.get("atom-python-run.disable_notifications_on_fail")) { - atom.notifications.add("warning", "You have to create the file first."); + atom.notifications.add("warning", "You have to create the file first."); //} return; } From 509f89538dfe4fccaa02cdb76001e864b5f6fb15 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Tue, 25 Oct 2016 20:17:29 +0800 Subject: [PATCH 11/77] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8af0f1d..58ec897 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -# atom-python-run package +# atom-python-run package +[![GitHub issues](https://img.shields.io/github/issues/foreshadow/atom-python-run.svg?style=plastic)](https://github.com/foreshadow/atom-python-run/issues) +[![GitHub stars](https://img.shields.io/github/stars/foreshadow/atom-python-run.svg?style=plastic)](https://github.com/foreshadow/atom-python-run/stargazers) +[![GitHub forks](https://img.shields.io/github/forks/foreshadow/atom-python-run.svg?style=plastic)](https://github.com/foreshadow/atom-python-run/network) +[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=plastic)](https://raw.githubusercontent.com/foreshadow/atom-python-run/master/LICENSE.md) Run your python source file (.py) using `F5` or `F6`! From 5050cca82f8c8771b23afc30fe9dcd0dfd565522 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Thu, 12 Jan 2017 18:57:00 +0800 Subject: [PATCH 12/77] v0.7.0 --- .gitignore | 1 + CHANGELOG.md | 4 ++ keymaps/atom-python-run.json | 4 +- lib/atom-python-run.js | 84 ++++++++++++++++++++++++++---------- 4 files changed, 68 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 1a353f5..280a0a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build +test .DS_Store npm-debug.log diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f1b18e..8eabf07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.7.0 +* Fix bug: multiple consoles +* New feature: add command setting with f5 and f6 + ## 0.6.2 * Fix bug: Uncaught TypeError: Cannot read property 'path' of null diff --git a/keymaps/atom-python-run.json b/keymaps/atom-python-run.json index 06ffd68..ac9ab33 100644 --- a/keymaps/atom-python-run.json +++ b/keymaps/atom-python-run.json @@ -1,6 +1,6 @@ { "atom-text-editor": { - "f5": "Python run: run", - "f6": "Python run: run" + "f5": "Python run: run-f5", + "f6": "Python run: run-f6" } } diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index e284e84..c2dbce1 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -1,60 +1,98 @@ /* global atom */ "use strict"; +const CompositeDisposable = require("atom").CompositeDisposable; const path = require("path"); const child_process = require("child_process"); module.exports = { activate: () => { - atom.commands.add("atom-text-editor", "Python run: run", run); + this.subscriptions = new CompositeDisposable(); + this.subscriptions.add( + atom.commands.add("atom-text-editor", "Python run: run-f5", run_f5), + atom.commands.add("atom-text-editor", "Python run: run-f6", run_f6) + ); + }, + deactivate: () => { + this.subscriptions.dispose(); }, config: { + f5_command: { + title: "Command of F5", + description: "{file} stands for current file path", + type: "string", + default: "python.exe {file}" + }, + f6_command: { + title: "Command of F6", + description: "{file} stands for current file path", + type: "string", + default: "python.exe {file}" + }, disable_notifications: { - title: "Disable notifications of success", - description: "Disable notifications of saving and running", - type: "boolean", - default: false + title: "Disable success notifications", + description: "Disable notifications while saving and running", + type: "boolean", + default: false }, disable_notifications_on_fail: { - title: "Disable notifications of failure", - description: "Disable notifications of extension name does not match", - type: "boolean", - default: false + title: "Disable failure notifications", + description: "Disable notifications when extension name does not match", + type: "boolean", + default: false } } }; -function run() { +function run_f5() { + run(atom.config.get("atom-python-run.f5_command")); +} + +function run_f6() { + run(atom.config.get("atom-python-run.f6_command")); +} + +function run(command) { var editor = atom.workspace.getActiveTextEditor(); if (!editor) { return; } var file = editor.buffer.file; if (!file) { - //if (!atom.config.get("atom-python-run.disable_notifications_on_fail")) { - atom.notifications.add("warning", "You have to create the file first."); - //} + notification("warning", "You have to create the file first.", true); return; } - if (!atom.config.get("atom-python-run.disable_notifications")) { - atom.notifications.add("info", "Saving..."); - } + notification("info", "Saving..."); editor.save(); var info = path.parse(file.path); if (info.ext != ".py") { - if (!atom.config.get("atom-python-run.disable_notifications_on_fail")) { - atom.notifications.add("warning", info.base + " is not a .py file, exit."); - } + notification("warning", format("{0} is not a .py file, exit.", [info.base])); return; } - if (!atom.config.get("atom-python-run.disable_notifications")) { - atom.notifications.add("info", "Running " + info.base + " ..."); - } + notification("info", format("Running {0} ...", [info.base])); var child = child_process.spawn("cmd", [ - "/c", "start", __dirname + "/../bin/cp.exe", "python.exe", "\"" + file.path + "\"" + "/c", "start", __dirname + "/../bin/cp.exe", format(command, { + "file": file.path + }) ], { cwd: info.dir, detached: true }); child.unref(); } + +function notification(type, message, always) { + if (type == "info") { + if (always || !atom.config.get("atom-python-run.disable_notifications")) { + atom.notifications.add("info", message); + } + } else if (type == "warning") { + if (always || !atom.config.get("atom-python-run.disable_notifications_on_fail")) { + atom.notifications.add("warning", message); + } + } +} + +function format(string, array) { + return string.replace(/{(.*?)}/g, k => array[k.substring(1, k.length - 1)]); +} From f8805f172182a727b242e86cf0ad2379dd72e965 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Thu, 12 Jan 2017 18:57:26 +0800 Subject: [PATCH 13/77] Prepare 0.7.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9af69b3..1221b2b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.6.2", + "version": "0.7.0", "description": "Run a python source file.", "keywords": [ "python" From fc0ae6c88fe2ad77ac3bd5bccbda0d663515f0a9 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Thu, 12 Jan 2017 19:08:36 +0800 Subject: [PATCH 14/77] Prepare 0.7.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1221b2b..42fe1ac 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.7.0", + "version": "0.7.1", "description": "Run a python source file.", "keywords": [ "python" From 8f23141d5fbc1a3f072140c6ebf89fb8282519c2 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Mon, 16 Jan 2017 01:25:56 +0800 Subject: [PATCH 15/77] Trying to fix bug --- lib/atom-python-run.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index fef0259..7cf95a8 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -21,13 +21,13 @@ module.exports = { title: "Command of F5", description: "{file} stands for current file path", type: "string", - default: "python.exe {file}" + default: "python.exe \"{file}\"" }, f6_command: { title: "Command of F6", description: "{file} stands for current file path", type: "string", - default: "python.exe {file}" + default: "python.exe \"{file}\"" }, disable_notifications: { title: "Disable success notifications", @@ -71,11 +71,15 @@ function run(command) { return; } notification("info", format("Running {0} ...", [info.base])); - var child = child_process.spawn("cmd", [ - "/c", "start", __dirname + "/../bin/cp.exe", format(command, { + var ca = command.split(" "); + ca.forEach(function(k, i, a) { + a[i] = format(k, { "file": file.path - }) - ], { + }); + }); + var child = child_process.spawn("cmd", [ + "/c", "start", __dirname + "/../bin/cp.exe" + ].concat(ca), { cwd: info.dir, detached: true }); From 71a916ec06356cd7bc567769b18001c7e864e089 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Mon, 16 Jan 2017 01:26:35 +0800 Subject: [PATCH 16/77] Prepare 0.7.2 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 42fe1ac..c045f43 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.7.1", + "version": "0.7.2", "description": "Run a python source file.", "keywords": [ "python" From 553ddf8a64fa64ac2a7ecab91b1650fc530d09e9 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Wed, 18 Jan 2017 21:37:58 +0800 Subject: [PATCH 17/77] 0.7.3 --- CHANGELOG.md | 8 +++++++- lib/atom-python-run.js | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eabf07..1f536ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ -## 0.7.0 +## 0.7.3 +* `{dir}` `{name}` `{ext}` is now supported. You can use `python.exe "{dir}\{name}{ext}"`, which is equal to `python.exe "{file}"`. + +## 0.7.2 +* Trying to fix bug: cannot find file in some environments + +## 0.7.0 (skipped due to apm bug) and 0.7.1 * Fix bug: multiple consoles * New feature: add command setting with f5 and f6 diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index 7cf95a8..de9dc21 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -74,7 +74,10 @@ function run(command) { var ca = command.split(" "); ca.forEach(function(k, i, a) { a[i] = format(k, { - "file": file.path + "file": file.path, + "dir": info.dir, + "name": info.name, + "ext": info.ext, }); }); var child = child_process.spawn("cmd", [ From c2a038fb4906c7704fcc95959dd84c1dd6f250b4 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Wed, 18 Jan 2017 21:39:39 +0800 Subject: [PATCH 18/77] Prepare 0.7.3 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c045f43..a6c6134 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.7.2", + "version": "0.7.3", "description": "Run a python source file.", "keywords": [ "python" From 56b943b481bdad9bcfce799552090c894ffd87b4 Mon Sep 17 00:00:00 2001 From: Austin Berrio Date: Thu, 1 Jun 2017 03:07:40 -0400 Subject: [PATCH 19/77] Added cross-platform compatability... --- bin/cp-darwin13.4.0-x86_64 | Bin 0 -> 10676 bytes bin/cp-darwin13.4.0-x86_64.dSYM.zip | Bin 0 -> 10676 bytes bin/cp-linux2.6.32-x86_64 | Bin 0 -> 17424 bytes bin/cp-win32.exe | Bin 0 -> 68441 bytes bin/cp.exe | Bin 47944 -> 0 bytes lib/atom-python-run.js | 51 ++++- lib/terminal.js | 340 ++++++++++++++++++++++++++++ src/cp.c | 175 ++++++++++++++ src/cp.cpp | 24 -- 9 files changed, 554 insertions(+), 36 deletions(-) create mode 100644 bin/cp-darwin13.4.0-x86_64 create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM.zip create mode 100755 bin/cp-linux2.6.32-x86_64 create mode 100644 bin/cp-win32.exe delete mode 100644 bin/cp.exe create mode 100644 lib/terminal.js create mode 100644 src/cp.c delete mode 100644 src/cp.cpp diff --git a/bin/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64 new file mode 100644 index 0000000000000000000000000000000000000000..835c1d10896d6b1bcc8b8461303b9d750a327e2c GIT binary patch literal 10676 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 E3j^=K+5i9m literal 0 HcmV?d00001 diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM.zip b/bin/cp-darwin13.4.0-x86_64.dSYM.zip new file mode 100644 index 0000000000000000000000000000000000000000..835c1d10896d6b1bcc8b8461303b9d750a327e2c GIT binary patch literal 10676 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 E3j^=K+5i9m literal 0 HcmV?d00001 diff --git a/bin/cp-linux2.6.32-x86_64 b/bin/cp-linux2.6.32-x86_64 new file mode 100755 index 0000000000000000000000000000000000000000..b0c16b7cfaf2a422709f5c1d3882925845825734 GIT binary patch literal 17424 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 literal 0 HcmV?d00001 diff --git a/bin/cp-win32.exe b/bin/cp-win32.exe new file mode 100644 index 0000000000000000000000000000000000000000..ea659984cb99aefe9311063841975190eb3eba06 GIT binary patch literal 68441 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 { @@ -21,13 +42,13 @@ module.exports = { title: "Command of F5", description: "{file} stands for current file path", type: "string", - default: "python.exe \"{file}\"" + default: "python {file}" }, f6_command: { title: "Command of F6", description: "{file} stands for current file path", type: "string", - default: "python.exe \"{file}\"" + default: "python {file}" }, disable_notifications: { title: "Disable success notifications", @@ -71,8 +92,9 @@ function run(command) { return; } notification("info", format("Running {0} ...", [info.base])); - var ca = command.split(" "); - ca.forEach(function(k, i, a) { + var cmds = command.split(" "); + cmds.forEach(function(k, i, a) { + k = new String(k); a[i] = format(k, { "file": file.path, "dir": info.dir, @@ -80,13 +102,18 @@ function run(command) { "ext": info.ext, }); }); - var child = child_process.spawn("cmd", [ - "/c", "start", __dirname + "/../bin/cp.exe" - ].concat(ca), { - cwd: info.dir, - detached: true - }); - child.unref(); + let log = true; + let options = { + cwd: info.dir, + detached: true, + }; + if ('win32' === process.platform) { + cmds = [log, options, `${__dirname}/../bin/cp.exe`, ...cmds]; + } else { + cmds = [log, options, ...cmds]; + } + let tty = terminal.type.spawns_tty(...cmds); + tty.unref(); } function notification(type, message, always) { diff --git a/lib/terminal.js b/lib/terminal.js new file mode 100644 index 0000000..b890bf3 --- /dev/null +++ b/lib/terminal.js @@ -0,0 +1,340 @@ +/* + terminal.js - cross-platform compatible base for executing 3rd party apps + Copyright (C) 2017 xovertheyearsx@gmail.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + + + Type() creates an instance that refers to the local machines graphical tty + (terminal, shell, prompt, etc...). + + The global value returned by this module is an instance created from Type + exports.type = new Type(); + + Assuming exports.type is called as + let terminal = require('/path/to/terminal'); + + terminal.type is an object that consists of 2 main properties. the rest + is handled by the constructor so you don't have to worry about calling + a new process for each system type. + + The constructor consists of 5 methods and 1 property object. + + NOTE: get_linux_terminal, set_linux_terminal, and has_a_tty are all handled + internally by the constructor and should not require any modifications. + + NOTE: if you want to modify the shell, options, or command properties, you + can do so with the has_a_tty property. + + terminal.type.get_linux_terminal() + returns the name of a linux compatible shell + + terminal.type.set_linux_terminal() + returns an object containing the name of the default shell and + an option to execute the following arguments + + terminal.type.has_a_tty() + returns an object representing the commands the shell should execute + + terminal.type.spawns_tty() + spawns a graphical tty and returns + + terminal.type.has_a_log() + outputs executed properties to built-in console. this is typically + executed each time a tty is spawned with terminal.type.spawns_tty(). + NOTE: there is a speed cost for executing this and should only be + handled by the constructor. + + terminal.type.has_a_tty + a property constructed by has_a_tty(). This property object is passed + to spawns_tty() internally and can be configured by modifying its + raw string properties + + the way terminal.type.has_a_tty is set is system dependent. + If you're on a Windows or Mac OS X machine, you'll have 3 properties. + If you're on a Linux machince, you'll have 2 properties. + + terminal.type + terminal.type.spawns_tty(...[arguments], {options}) + a basic wrapper for nodes child_process.spawn() + https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options + + returns a based on the [arguments] and {options} + parameters. [arguments] should be passed as ...array and {options} + should be passed as {object}. [arguments] can be passed as seperate + primitive string based arguments as well. + + for example, + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator + + const terminal = require('./terminal'); + + var tty = terminal.type.spawns_tty( + { + cwd: undefined, + detached: true, + }, + "python", + "-c", + "print('Hello, World!');input('...enter to continue...')" + ); + + or with the spread operator + + var commands = [ + "python", "-c", + "print('Hello, World!');input('...enter to continue...')" + ]; + + var tty = terminal.type.spawns_tty( + { + cwd: undefined, + detached: true, + }, + ...commands + ); + + terminal.type.has_a_tty + this property has 2 - 3 primitive string based properties depending + on your system. + + terminal.type.has_a_tty.shell + the graphical tty executable (or the shell it self) + + terminal.type.has_a_tty.option + the options to be passed to the graphical tty executable + + terminal.type.has_a_tty.command + For win32 and darwin only. Usually Windows or Mac OS X will need + a special argument in order to execute a 3rd party executable. + + for win32 (Windows) + cmd /c start file-to.exe + for darwin (Mac OS X) + osascript -e 'tell app "Terminal" to do script "/path/to/executable"' + + terminal.type.has_a_tty.shell + compatible shells listed by system type + + "linux": + ["gnome-terminal", "konsole", "terminator", "xfce4-terminal", + "lxterminal", "uxterm", "xterm"] + uses default based on current desktop environment + + "win32": + ["cmd","powershell","bash"] + defaults to cmd + + "darwin": + ["osascript", "terminal"] + defaults to osascript +*/ +'use strict'; + +const spawn = require('child_process').spawn; + +let get_linux_terminal = function () { + switch (process.env.GDMSESSION) { + // if session is using gtk + case 'ubuntu': + case 'ubuntu-2d': + case 'gnome': + case 'gnome-shell': + case 'gnome-classic': + case 'gnome-fallback': + case 'cinnamon': + return "gnome-terminal"; + // xfce session has its own terminal, xfce is gtk compatible + case 'xfce': + return "xfce4-terminal"; + // if session is using qt, kde and lxde are qt compatible + case 'kde-plasma': + return "konsole"; + case 'Lubuntu': + return "lxterminal"; + default: + // attempt to determine desktop session + switch (process.env.XDG_CURRENT_DESKTOP) { + case 'Unity': + case 'GNOME': + case 'X-Cinnamon': + return "gnome-terminal"; + case 'XFCE': + return "xfce4-terminal"; + case 'KDE': + return "konsole"; + case 'LXDE': + return "lxterminal"; + // if unknown session, default to xterm + default: + return "xterm"; + } + } +}; + +let get_linux_option = function (shell) { + // determine the shells default execution option + switch (shell) { + case 'gnome-terminal': + case 'xfce4-terminal': + return "-x" + case 'konsole': + case 'lxterminal': + case 'xterm': + return "-e" + default: + return "-e" + } +}; + +let LinuxTTY = function () { + // linux has a mass variety of environments and a variety of ways + // for determining those environments. best to go for the base + // environments by depending on the most common builtin shells instead. + // https://askubuntu.com/questions/72549/how-to-determine-which-window-manager-is-running + let terminal = get_linux_terminal(); + this.shell = terminal; + this.option = get_linux_option(terminal); + this.command = `${process.env.HOME}/.atom/packages/atom-python-run/bin/cp-linux2.6.32-x86_64`; +}; + +let DarwinTTY = function () { + // NOTE: osascript should always be used! consider your self warned! + // the default shell is determined by the Terminal.app settings. + // the path method must be called in order to generate a valid + // command every time it is called. the command property contains + // an empty primitive string otherwise. + this.shell = 'osascript'; + this.option = '-e'; + this.command = function (...args) { + let path = `${process.env.HOME}/.atom/packages/atom-python-run/bin/cp-darwin13.4.0-x86_64`; + for (let token of args) { + path += ` ${token}`; + } + return `tell app "Terminal" to do script "${path}"` + }; +}; + +let Win32TTY = function () { + // windows xp and up can be gaurenteed to have the cmd.exe shell. + // %comspec% should default to %windir%\system32\cmd.exe unless + // otherwise modified by the end user. + // https://en.wikipedia.org/wiki/Environment_variable#Windows + this.shell = process.env.COMSPEC; + this.option = '/c'; + this.command = `start ${process.env.HOME}\\.atom\\packages\\atom-python-run\\bin\\cp-win32`; +}; + +let has_a_tty = function () { + switch (process.platform) { + case 'linux': + return new LinuxTTY(); + case 'darwin': + return new DarwinTTY(); + case 'win32': + return new Win32TTY(); + } +}; + +let has_a_log = function (tty, options, ...args) { + console.log( + `platform: ${process.platform}\n` + + `has_a_tty:\n\tshell: ${this.has_a_tty.shell}\n` + + `\toption: ${this.has_a_tty.option}\n` + ); + if ("darwin" === process.platform) { + console.log(`\tcommand: ${this.has_a_tty.command(...args)}\n`); + } else { + console.log(`\tcommand: ${this.has_a_tty.command}\n`); + } + console.log( + "spawn_tty:\n" + + `\toptions:\n${JSON.stringify(options, null, 4)}\n` + + `\targs: ${[...args]}\n` + ); + tty.stdout.on('data', (data) => { + console.log(`stdout: ${data}`); + }); + tty.stderr.on('data', (data) => { + console.log(`stderr: ${data}`); + }); + tty.on('close', (code) => { + console.log(`child process exited with code ${code}`); + }); +}; + +let is_empty = function (object) { + for(var property in object) { + if(object.hasOwnProperty(property)) { + return false; + } + } + return JSON.stringify(object) === JSON.stringify({}); +} + +let set_up_default = function (options) { + if (is_empty(options)) { + return { + 'cwd': undefined, + 'detached': false, + }; + } + return options +} + +let spawns_tty = function (log, options, ...args) { + let tty; + options = set_up_default(options); + switch (process.platform) { + case 'linux': + case 'win32': + tty = spawn( + this.has_a_tty.shell, + [ + this.has_a_tty.option, + this.has_a_tty.command, + ...args, + ], + options + ) + break; + case 'darwin': + // darwin should never be modified or configured in any way. doing + // so risks breaking the required tool chain. + // NOTE: there is a weird bug that causes multiple windows to spawn. + tty = spawn( + this.has_a_tty.shell, + [ + this.has_a_tty.option, + this.has_a_tty.command(...args), + ], + options + ); + break; + } + if (log) { + this.has_a_log(tty, options, ...args); + } + return tty; +}; + +let Type = function () { + this.has_a_tty = has_a_tty(); + this.has_a_log = has_a_log; + this.spawns_tty = spawns_tty; +}; + +exports.type = new Type(); diff --git a/src/cp.c b/src/cp.c new file mode 100644 index 0000000..9527094 --- /dev/null +++ b/src/cp.c @@ -0,0 +1,175 @@ +/* + compiled as + gcc -g -std=c99 -Wall cp.c -o cp + Windows + using mingw (Minature GNU for Windows) toolchain + target win32 + gcc version 5.3.0 (GCC) + not stripped + NOTE: this cp.exe version should not require a local dll. + if it does, it requires the MSVCRT.DLL + + Linux + using gcc toolchain + target x86_64-linux-gnu + gcc version 5.4.0 20160609 + Ubuntu 5.4.0-6ubuntu1~16.04.4 + not stripped + + Mac OS X + using clang toolchain target x_86_64-apple-darwin13.4.0 + Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) + Mach-O 64-bit executable + not stripped +*/ +#include +#include +#include +#include + +#ifndef BUFFER +# define BUFFER 4096 +#endif + +typedef _Bool bool; + +const bool true = 1; +const bool false = 0; + +void logs(char * string); + +void log_system_type(void); + +void log_exe_type(const char * exe); + +void log_arg_tokens(int index, char ** argv); + +void log_last_element(int index); + +void log_cmd_type(const char * exe, char * cmd); + +void log_file_read(int index, char ** argv); + +bool is_last_element(int index, int argc); + +bool is_readable_file(int index, char **argv); + +void system_command_read(void); + +int main(int argc, char *argv[]) +{ + char cmd[BUFFER]; + const char exe[] = "python"; + log_system_type(); + memset(cmd, '\0', sizeof(cmd)); + if (strncmp(exe, argv[1], strlen(exe))) { + log_exe_type(exe); // any version of python should execute + return 1; + } + for (int i = 1; i < argc; i++) { + log_arg_tokens(i, argv); + if (is_last_element(i, argc) && is_readable_file(i, argv)) { + log_last_element(i); + strcat(cmd,"\""); + strcat(cmd, argv[i]); + strcat(cmd,"\""); + } else { + strcat(cmd, argv[i]); + strcat(cmd, " "); + } + } + int t = clock(); + int r = system(cmd); // origin command + t = clock() - t; + log_cmd_type(exe, cmd); + printf( + "\nProcess returned %d (0x%X)\texecution time : %.3f s\n", + r, r, t / 1000. + ); + system_command_read(); + return r; +} + +/* + i assume the last argument is always a file + this can be modified to be less strict by removing is_readable_file() from the if clause + example -> if (is_last_element(i, argc) && is_readable_file(i, argv)) +*/ +bool is_last_element(int index, int argc) { + return ((argc - 1) == index) ? true: false; +} + +bool is_readable_file(int index, char **argv) { + FILE * fp; + if (NULL == (fp = fopen(argv[index], "r"))) { + log_file_read(index, argv); + return false; + } + fclose(fp); + return true; +} + +void system_command_read(void) { +#ifdef _WIN32 + system("pause"); /*"Press any key to continue.\n"*/ +#elif __APPLE__ + system("echo \"Close this window to continue...\""); +#else + system("printf 'Press [ENTER] to continue...'; read _;"); +#endif +} + +void logs(char * string) { + FILE * file = fopen("cp.log", "a+"); + if (NULL == file) + { + perror("Error"); + exit(1); + } + fputs(string, file); + fclose(file); +} + +void log_system_type(void) { +#ifdef _WIN32 + logs("System Type: win32\n"); +#elif __APPLE__ + logs("System Type: macosx\n"); +#elif __linux__ + logs("System Type: linux\n"); +#elif __unix__ + logs("System Type: unix\n"); +#else + logs("System Type: unknown\n"); +#endif +} + +void log_exe_type(const char * exe) { + char string[BUFSIZ]; + sprintf(string, "Error: I can only execute %s programs =p\n", exe); + logs(string); +} + +void log_arg_tokens(int index, char ** argv) { + char string[BUFSIZ]; + sprintf(string, "Log: argv[%i] '%s'\n", index, argv[index]); + logs(string); +} + +void log_last_element(int index) { + char string[BUFSIZ]; + sprintf(string, "Log: index [%i] is_last_element and is_readable_file\n", index); + logs(string); +} + +void log_cmd_type(const char * exe, char * cmd) { + char string[BUFSIZ]; + sprintf(string, "Log: cmd '%s'\n", cmd); + logs(string); +} + +void log_file_read(int index, char ** argv) { + char string[BUFSIZ]; + sprintf(string, "Error: I can't read the file '%s'.", argv[index]); + logs(string); +} diff --git a/src/cp.cpp b/src/cp.cpp deleted file mode 100644 index 3698f34..0000000 --- a/src/cp.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - char cmd[4096]; - strcpy(cmd, "title "); // prepend title - for (int i = 1; i < argc; i++) { - strcat(cmd, argv[i]); - strcat(cmd, " "); - } - system(cmd); // reset title - int t = clock(); - int r = system(cmd + 6); // origin command - t = clock() - t; - printf("\n" - "Process returned %d (0x%X) execution time : %.3f s\n" - /*"Press any key to continue.\n"*/, - r, r, t / 1000.); - system("pause"); - return r; -} From e6dc2b92760d69ad5e7c2976b9a483414d051e82 Mon Sep 17 00:00:00 2001 From: Austin Berrio Date: Fri, 2 Jun 2017 23:28:55 -0400 Subject: [PATCH 20/77] Added Cross-Platform Compatability and fixed most major bugs. --- bin/cp-darwin13.4.0-x86_64 | Bin 10676 -> 15888 bytes bin/cp-darwin13.4.0-x86_64.dSYM.zip | Bin 10676 -> 0 bytes .../Contents/Info.plist | 20 ++ .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 0 -> 12892 bytes .../Contents/Info.plist | 20 ++ .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 0 -> 12892 bytes .../Contents/Info.plist | 20 ++ .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 0 -> 12892 bytes .../Contents/Info.plist | 20 ++ .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 0 -> 13536 bytes .../Contents/Info.plist | 20 ++ .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 0 -> 12845 bytes .../Contents/Info.plist | 20 ++ .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 0 -> 12892 bytes bin/cp-linux2.6.32-x86_64 | Bin 17424 -> 19520 bytes bin/cp-win32.exe | Bin 68441 -> 73184 bytes bin/libgcc_s_dw2-1.dll | Bin 43008 -> 0 bytes lib/atom-python-run.js | 8 +- lib/terminal.js | 23 +- src/cp.c | 209 +++++------------- src/cplib.c | 137 ++++++++++++ src/cplib.h | 42 ++++ 22 files changed, 377 insertions(+), 162 deletions(-) mode change 100644 => 100755 bin/cp-darwin13.4.0-x86_64 delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM.zip create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist create mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 delete mode 100644 bin/libgcc_s_dw2-1.dll create mode 100644 src/cplib.c create mode 100644 src/cplib.h diff --git a/bin/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64 old mode 100644 new mode 100755 index 835c1d10896d6b1bcc8b8461303b9d750a327e2c..484695c23aa74a58e70ce3dfad11d7d324b454e0 GIT binary patch literal 15888 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 literal 10676 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 E3j^=K+5i9m diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM.zip b/bin/cp-darwin13.4.0-x86_64.dSYM.zip deleted file mode 100644 index 835c1d10896d6b1bcc8b8461303b9d750a327e2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10676 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 E3j^=K+5i9m diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist b/bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist new file mode 100644 index 0000000..c590b95 --- /dev/null +++ b/bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 new file mode 100644 index 0000000000000000000000000000000000000000..3ea9e34d3ba19c88e9fc1c72beeef687d4e233dc GIT binary patch literal 12892 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 + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 new file mode 100644 index 0000000000000000000000000000000000000000..3ea9e34d3ba19c88e9fc1c72beeef687d4e233dc GIT binary patch literal 12892 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 + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 new file mode 100644 index 0000000000000000000000000000000000000000..3ea9e34d3ba19c88e9fc1c72beeef687d4e233dc GIT binary patch literal 12892 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 + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 new file mode 100644 index 0000000000000000000000000000000000000000..0bbec3c68c442f4ff41c7a2658440a06939236fc GIT binary patch literal 13536 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 + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 new file mode 100644 index 0000000000000000000000000000000000000000..2d273cb82c5580166166831cf788ca8ab5da86e7 GIT binary patch literal 12845 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 + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 new file mode 100644 index 0000000000000000000000000000000000000000..3ea9e34d3ba19c88e9fc1c72beeef687d4e233dc GIT binary patch literal 12892 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 diff --git a/bin/cp-win32.exe b/bin/cp-win32.exe index ea659984cb99aefe9311063841975190eb3eba06..4142ca28fe45f13c77e61965836abe3d2f2a5451 100644 GIT binary patch delta 19492 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

GZ|PIDB35`^otJt(op> z7SRY1)+{&TW-w>clrQ2$^E{6;k$vKexcWka} zgg4M$-wg)Rp+iOkF<1H0OafB1%on+rxgPaJvTV%um@iZWq)dk_0Ft!Am!_JLmA=Rm zjMTUhcN^;$(~8q^_&DUX)3*dkSn1eXe)6p{m0XN+G$N{j0Zc69zreF3&qh&)jB2=! zCha~=#d>zfY>uAP0t|Q{PNvA$J={g$z;~PCq|LbUKuA_;$j^;7^N#h>`@xEFS zOFO5{)*nl{WzV%{Rp{mZ>9LJ;^iH53+}Je zgSa=+len*?7Tjy-bKIBGZ@AB=KL00PE}==d=TScHbEyvZ>GUSO?m3_o&R z#Z`LhOgyc)7)IZn38S57!bH7+#MwA{9Z>Sw?zH4=IP%@asN>Y~O+0P=CQOVp(A95x zQQJ4Ba6Sa;H65lyXXELGvte=&Z9f|?zVo9m&c=xoest|@oP3!2ejP72Q!4HaREYZu zT8;Yy^cL>3=u_M$(=WJ>q#Mq~%W*W}Ts&<$7e@2Xg<)4&doEu5AZW|EIB`MHv2$_q z3Hk|itrUGeUVfGSaXy{~oe$&o6m85?FXdqr=3=aW2eO^3$a*nOm;zyZFIsxh1TWgo#1WkdcWgnuYAxIFJ4$?(W<*FfTek9R zL{+VOdSRMKgkG}_NdUrMYvThe#d=A)1xT7U7P&o=Gy@2~mQ*D(k_RNo@wl((1uRHM zR0D)B7NQFJ4$6*nnH=auK?f;v=Z;&#yR9#d-=4q(}Mk^gFqZYKW_L=hhK zHsv(~Rs;y+*;7^jC1`4NqAvnz@Q4PXXFPp8D^Ij6+KO*=!28*Jmq1!Y6a6}n3?1?=kOa_rBjz89Xsb_roCZ>$lY9ZlNS&M?n2Sbrba$lbC{1`ba6*Sf z0m;;PE)K{#PcPYubBnXf7D{b8n$e))A4R=(P6C2ct`9N`NKCS4ePTfsxpwJup8B?y|CrO7W|IlFp=6pfPyNX08Sx z%3|$x?MHxa)w%y0AUQg=pMZ?fA%1v!SEf@I%s-7|*7Qmq07O5F-lWhtd}pmGfY{M0 zn@#(y=BTc%(?L_CQ$J4;LJdHzuU`W)SYTJLPEWS1d8Orcgov&k zZ-Z#Jr#o2mr}X!q;vL@tPths(8HiIy8HD$gWjdN(K+KTgHbjyJ0J)-T-UuL%>EzrB zWH7k2agBwnWOjCOS&fb@lLhHaUjoG9VM9h*4dhmxLC*kb)hXLUuU(FJd=9)rC+#~R zAoS@_f9_2k(i4dLjRN}_pPm76k1iD%1|(A_`DP$h{NfVyD$T79Kzw-@jyXVLFe7?L zVJVQSI@6bET;7;_21t#LX47>v2Y?*V(R>n$J;~D^Kk?2%c?GBQ_t#*%qGS67ND_?J zx=A^lVW8;R+8u9#cj^!`5dB2)m+lyoZ91a+K*XP^c>Phvf^^6VAa;+@?7ja0qW9T5 zKn_5NSI$R3_-vs1)pl=DZf==%q@z`*-U%Y^MQ_;t2&7R*(-m*M;Q*iIs5cPIrkX#H zk;VWi^LSDF;a{rqAWSn6G>iGwyw}DwKvlZ7mH?URp;;V8Ilugq9KZCX?|zMVfPcpK z{n$h)dp-wIN0UbVR0EwefYBQ?VtVRK5|#3tWJGzha2SuB2Yn; X?+yQ>CP02jrBHV~L2K0pZ{+^`HkF5jEs> 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 N!2kE}|4$V7{{Yh0Vj} -#include -#include -#include - -#ifndef BUFFER -# define BUFFER 4096 -#endif - -typedef _Bool bool; - -const bool true = 1; -const bool false = 0; - -void logs(char * string); - -void log_system_type(void); - -void log_exe_type(const char * exe); - -void log_arg_tokens(int index, char ** argv); - -void log_last_element(int index); - -void log_cmd_type(const char * exe, char * cmd); +#include "cplib.h" -void log_file_read(int index, char ** argv); - -bool is_last_element(int index, int argc); - -bool is_readable_file(int index, char **argv); - -void system_command_read(void); +extern const bool true; +extern const bool false; int main(int argc, char *argv[]) { - char cmd[BUFFER]; + // program to execute const char exe[] = "python"; - log_system_type(); - memset(cmd, '\0', sizeof(cmd)); + // dont execute unless arguments are present + if (3 > argc) { + fprintf(stderr, "Usage: %s %s [source-file]", argv[0], exe); + return 2; // invalid arguments + } + // the logs (absolute) path and filename + const char * logfile = make_logpath(); + // the python source file name + const char * filename = make_filename(argc, argv); + // concatenate param tokens to command + // and init the command buffer + const char * cmd = make_command(filename, argv); + // keep the log file from becoming needlessly large in size + remove(logfile); + // start logging data + log_file_path(logfile, logfile); // log the file path and name for log + log_file_path(logfile, filename); // log the path to the python src file + log_cmd_type(logfile, cmd); // log the command to be executed + log_system_type(logfile); // log the systems information + log_cp_version(logfile, "v0.7.3"); // cp version information + log_arg_tokens(logfile, argc, argv); // log all the tokens + // any version of python (and only python) should execute if (strncmp(exe, argv[1], strlen(exe))) { - log_exe_type(exe); // any version of python should execute - return 1; + log_exe_type(logfile, exe); + return 4; // invalid command type } - for (int i = 1; i < argc; i++) { - log_arg_tokens(i, argv); - if (is_last_element(i, argc) && is_readable_file(i, argv)) { - log_last_element(i); - strcat(cmd,"\""); - strcat(cmd, argv[i]); - strcat(cmd,"\""); - } else { - strcat(cmd, argv[i]); - strcat(cmd, " "); - } + // attempt to read the source file before executing it + if (false == is_readable_file(filename)) { + log_file_read(logfile, filename); + return 8; // failed to read source file } - int t = clock(); - int r = system(cmd); // origin command - t = clock() - t; - log_cmd_type(exe, cmd); - printf( - "\nProcess returned %d (0x%X)\texecution time : %.3f s\n", - r, r, t / 1000. - ); - system_command_read(); + // time the execution + int r = execute(cmd); + // pause screen according to os type + prompt(); + // return the child process exit code return r; } - -/* - i assume the last argument is always a file - this can be modified to be less strict by removing is_readable_file() from the if clause - example -> if (is_last_element(i, argc) && is_readable_file(i, argv)) -*/ -bool is_last_element(int index, int argc) { - return ((argc - 1) == index) ? true: false; -} - -bool is_readable_file(int index, char **argv) { - FILE * fp; - if (NULL == (fp = fopen(argv[index], "r"))) { - log_file_read(index, argv); - return false; - } - fclose(fp); - return true; -} - -void system_command_read(void) { -#ifdef _WIN32 - system("pause"); /*"Press any key to continue.\n"*/ -#elif __APPLE__ - system("echo \"Close this window to continue...\""); -#else - system("printf 'Press [ENTER] to continue...'; read _;"); -#endif -} - -void logs(char * string) { - FILE * file = fopen("cp.log", "a+"); - if (NULL == file) - { - perror("Error"); - exit(1); - } - fputs(string, file); - fclose(file); -} - -void log_system_type(void) { -#ifdef _WIN32 - logs("System Type: win32\n"); -#elif __APPLE__ - logs("System Type: macosx\n"); -#elif __linux__ - logs("System Type: linux\n"); -#elif __unix__ - logs("System Type: unix\n"); -#else - logs("System Type: unknown\n"); -#endif -} - -void log_exe_type(const char * exe) { - char string[BUFSIZ]; - sprintf(string, "Error: I can only execute %s programs =p\n", exe); - logs(string); -} - -void log_arg_tokens(int index, char ** argv) { - char string[BUFSIZ]; - sprintf(string, "Log: argv[%i] '%s'\n", index, argv[index]); - logs(string); -} - -void log_last_element(int index) { - char string[BUFSIZ]; - sprintf(string, "Log: index [%i] is_last_element and is_readable_file\n", index); - logs(string); -} - -void log_cmd_type(const char * exe, char * cmd) { - char string[BUFSIZ]; - sprintf(string, "Log: cmd '%s'\n", cmd); - logs(string); -} - -void log_file_read(int index, char ** argv) { - char string[BUFSIZ]; - sprintf(string, "Error: I can't read the file '%s'.", argv[index]); - logs(string); -} diff --git a/src/cplib.c b/src/cplib.c new file mode 100644 index 0000000..9daffa8 --- /dev/null +++ b/src/cplib.c @@ -0,0 +1,137 @@ +#include "cplib.h" + +const bool true = 1; +const bool false = 0; + +void logs(const char * path, char * string) { + FILE * file = fopen(path, "a"); + if (NULL == file) + { + fprintf( + stderr, + "Error: in logs() function...\npath: '%s'\nstring: '%s'\n", + path, string + ); + perror("Error"); + exit(1); + } + fputs(string, file); + fclose(file); +} + +void log_cp_version(const char * path, const char * version) { + char string[BUFSIZ]; + sprintf(string, "Log: cp-executable version: %s\n", version); + logs(path, string); +} + +void log_exe_type(const char * path, const char * exe) { + char string[BUFSIZ]; + sprintf(string, "Error: exe type: %s programs only =p\n", exe); + logs(path, string); +} + +void log_arg_tokens(const char * path, int argc, char ** argv) { + char string[BUFSIZ]; + for (int index = 0; index < argc; index++) { + sprintf(string, "Log: argv[%i]: '%s'\n", index, argv[index]); + logs(path, string); + } +} + +void log_file_path(const char * path, const char * filename) { + char string[BUFSIZ]; + sprintf(string, "Log: file path and name: '%s'\n", filename); + logs(path, string); +} + +void log_cmd_type(const char * path, const char * cmd) { + char string[BUFSIZ]; + sprintf(string, "Log: cmd: '%s'\n", cmd); + logs(path, string); +} + +void log_file_read(const char * path, const char * filename) { + char string[BUFSIZ]; + sprintf(string, "Error: file: '%s' is not readable.\n", filename); + logs(path, string); +} + +void log_system_type(const char * path) { +#ifdef _WIN32 + logs(path, "Log: system type: win32\n"); +#elif __APPLE__ + logs(path, "Log: system type: darwin\n"); +#elif __linux__ + logs(path, "Log: system type: linux\n"); +#elif __unix__ + logs(path, "Log: system type: unix\n"); +#else + logs(path, "Log: system type: unknown\n"); +#endif +} + +void prompt(void) { +#ifdef _WIN32 + system("pause"); /*"Press any key to continue.\n"*/ +#elif __APPLE__ + system("echo \"Close this window to continue...\""); +#else + system("printf 'Press [ENTER] to continue...'; read _;"); +#endif +} + +bool is_readable_file(const char * filename) { + FILE * file = fopen(filename, "r"); + if (NULL == file) { + perror("Error"); + return false; + } + fclose(file); + return true; +} + +char * make_logpath(void) { + char * logpath = malloc(BUFSIZ); + #ifdef _WIN32 + sprintf(logpath, "%s\\.atom\\packages\\atom-python-run\\bin\\cp.log", getenv("USERPROFILE")); + #else + sprintf(logpath, "%s/.atom/packages/atom-python-run/bin/cp.log", getenv("HOME")); + #endif + return logpath; +} + +char * make_filename(int argc, char **argv) { + char * filename = malloc(BUFSIZ); + memset(filename, '\0', BUFSIZ); + for (int i = 2; i < argc; i++) { + strcat(filename, argv[i]); + if ((argc - 1) != i) { + strcat(filename, " "); + } + } + return filename; +} + +char * make_command(const char * filename, char ** argv) { + char * cmd = malloc(BUFFER); + memset(cmd, '\0', BUFFER); + strcat(cmd, argv[1]); + strcat(cmd, " "); + strcat(cmd, "\""); + strcat(cmd, filename); + strcat(cmd, "\""); + return cmd; +} + +int execute(const char * command) { + int t = clock(); + int r = system(command); // origin command + t = clock() - t; + // output results to user + printf( + "\nProcess returned %d (0x%X)\texecution time : %.3f s\n", + r, r, t / 1000. + ); + return r; +} diff --git a/src/cplib.h b/src/cplib.h new file mode 100644 index 0000000..7fc16ee --- /dev/null +++ b/src/cplib.h @@ -0,0 +1,42 @@ +#ifndef cplib +# define cplib + +#include +#include +#include +#include + +# ifndef BUFFER +# define BUFFER 4096 +# endif + +typedef _Bool bool; + +void prompt(void); + +bool is_readable_file(const char * filename); + +char * make_filename(int argc, char **argv); + +char * make_command(const char * filename, char ** argv); + +char * make_logpath(void); + +int execute(const char * command); + +void logs(const char * path, char * string); + +void log_cp_version(const char * path, const char * version); + +void log_system_type(const char * path); + +void log_exe_type(const char * path, const char * exe); + +void log_arg_tokens(const char * path, int argc, char ** argv); + +void log_file_path(const char * path, const char * filename); + +void log_cmd_type(const char * path, const char * cmd); + +void log_file_read(const char * path, const char * filename); +#endif From 4dab38ad47efb211aaad629a207196479821b900 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Sun, 23 Jul 2017 18:41:05 +0800 Subject: [PATCH 21/77] Too long filename --- .../Contents/Info.plist | 20 --- .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 12892 -> 0 bytes .../Contents/Info.plist | 20 --- .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 12892 -> 0 bytes .../Contents/Info.plist | 20 --- .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 12892 -> 0 bytes .../Contents/Info.plist | 20 --- .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 13536 -> 0 bytes .../Contents/Info.plist | 20 --- .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 12845 -> 0 bytes .../Contents/Info.plist | 20 --- .../Resources/DWARF/cp-darwin13.4.0-x86_64 | Bin 12892 -> 0 bytes bin/cp.log | 8 ++ lib/atom-python-run.js | 130 +++++++++--------- 14 files changed, 76 insertions(+), 182 deletions(-) delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist delete mode 100644 bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 create mode 100644 bin/cp.log diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist b/bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist deleted file mode 100644 index c590b95..0000000 --- a/bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Info.plist +++ /dev/null @@ -1,20 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleIdentifier - com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - dSYM - CFBundleSignature - ???? - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 deleted file mode 100644 index 3ea9e34d3ba19c88e9fc1c72beeef687d4e233dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12892 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 - - - - CFBundleDevelopmentRegion - English - CFBundleIdentifier - com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - dSYM - CFBundleSignature - ???? - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 deleted file mode 100644 index 3ea9e34d3ba19c88e9fc1c72beeef687d4e233dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12892 zcmeHNdvuh=6`z?e8xl6jhI~uFCz0h5D`Zmy3@Vf$K_b+0fRae<`P|)Xl9kQwcK2Hn zFeoV6P_2Tmg2&eSYO8&SmQoaYD)b?J99un7+a5i|BduulNLvf1^(g(_d2Dt!g8j4S zbmwF;bMKw|n>%;zH#^_V_nUve^WiAQxWyP7jf^^LtiV5=&O$jJx#iG-chB87=f$lr z%pLc^zojA_Qw$7D> zEu?-!AF#Vxk)sK%ohHi=^WREgrQ1!HlXg{>=F^IA3Nd6dDP$JbSgH^Md8j+2Nx znRR{PWZX%)=~Lq6!x>$Ir1-!il}nD(lN_Gak@nR#DEsolIA;ri5S~pHog60~I-Pyv z8x-D$9w!^@j|IoJnG270K6YbnBO`53&@_g0dYJW%pL$pa-1lsr)K z!2gm5XnRzH7v4+tdt4Q0A6J`n+L|;F4f!QnK2^#Y+c{3{H)d%%g^fG4ryaWwm7SXI z)bz7>0V8>@q0dw_w&t>ZvzBkx^hO*!Q2V=#T(^Hz)3v(&ZX-Wiwcn>{^Im^I)7NOc zmx;!B%zH3)K7FBBqA5BM#U7}y*Fhzcb)$GA4|U;<-02R*GZ6JeT_>LGaZ(|-mpQQv zUY|0q6OBjvq6t?r(ohJ6v4!l8#i2lrL+KvJO|FS1@V-EFq_4}N4WXjYQyxWH4q+%^ zAv`H()@9si->?QS$#P}{kz`+A2rt$O!%b)P8!-l4N+l1JJW%pL$pa-1lsr)KK*<9o z50pGm@<7Q0|Mwo?WBBA6+hf(#mY-EK25;PACbMeV?N3!*?7hS*@JWC*g|*}6u&f@1 zRYP3duunpxd0sE7F5tEnficOI%WOm=Uazv>t_D=QO+?G+ z8Nl}Y5D8<)qVK6V0jc*$$Z>pPz}T=G*{3p%0Vzs!`h^KaYf$Ra)jus^WgBL^{^Cqa76wO7cbc-r9D!qw*U> zE_hjz$DGQ?iEKNf$d1aVh;)?h?>j1Y5qXUwU+Nfr(^!yTy1b6o2mi{MH3NlbANb|Gn#5(E^8KXd@>VLina#)a85mzx@q&~Stk^cF#^Wk# zfR)pk6d%yaX6h&DDK^Rjy15$t0F7R3OI02v-F3$$)yr!BO5}~4a}yC8^tl&bReq9W zw{aU2&AEwKpN>>?Qw8hzZ6p@k2AGdVS!)yT9^jb>GT*nL{dg&(_L!A^<@bBF-}@^k zkaRm4hDqvYHIs;b2uSAHn3(Iib|%SoQZJhKR8Cg0ovhyZH;IE@WT#J>URZ4_sqTiV zd8B%aoW5Jl<=g$ral3WcXrvzwBP}DOePo38{F6m9u+JbVBYrfj8Y0yLWQ10^D(7`& z{{drvg$&jM%4vtS5kDD5N)XaPAekpPa5fm^0v$sKjS*@%2MdSuiyV@Vy71B*j-jYM z>B}c9=A(G_y$`RJmE$yY5R~{z-6j;<#els9GdKop4$%_-5W%pk=x$^*-z3NMWC76yFd;Q zd6NjVaCf%)9t5AqQrZx(^Y+QK~*RrVPur6)V2CntcEW^|G#@vE}|s$+@6 z>&XwycY7hDo~E&$8Zh5uB%+R0+j^=hQQ0q1os5=ZN!cM(r=ePwC_9YmC8%0Wo;DPo z3g6q{AkdcTxM&Hb%=YJ_40>dhoJ|Osppn?P`$C zX<^OuCeyB5kkBri?UsvAcPg22Tt*K*K7|UU@lBQ^V4wt3KVZHk!`Fl8J`dP0bCBkvKkYn?Ad_xv9Bn)gf%BHUrgQD4TI(iQvL?G8Ik^1Xm=p=}0tqdCXm$4F^MRvTu57o!gsC zOiyPM!Av@$Vnq5$`ACI|3=;|wOihITtz~dN&5XQHb6J|jr01kxiem_0ltu?AvI?W9 zJN1!4R1iS$Xg4nUWDMy~U1g6H6Zv;;MCam&h`bmgvLJqlk&7K7hT}%gw3%@y#T;BR z_T!#09zqc7%foOtZUMvHnb>;V6-JWDH8CpVNxF$t_j{RWbPe>K28V3NIa64$U{N6scn!VnyLK99o58rAO4s7O!Ysj38(9 zMn#$4pC}QJ%kNNC9S;CIiG=u`~f}3>dlm5iNK8C~4`VB)9D-Y1N}N2_;o&O`{~Y;V5abqeL%aB&Q{p zlHA&(JfihyYJFP&D1|gl%NnItP1ABniQd&nl~!&_W)7=Sr;P2mOf*+ChZerE_b@MN zG+Sz(Xs!r@N=gJx-BAnrI+x}XFaFSizxE&h;M?mLy0cEEPBd>E@*X<2x^~5})Z4XH zYj^$bwd>bM8txg~_WipDj|E?;dGx8xgJ1vVul{uqdVfMbj$Dn7g2;=IKaWgvb}RBj z$WJ5x7WoZi+7qFWD<l^aw}X<~CSuc~3ikOYd~1lk2bl&# zYxyL^MB~MBbEK=(f;N4mNUJs2?YE34M={z25}WptHe;91d<`#@jL}|<*t9gG_2xsL zTSDwj;1QdC5YYOHM^4Dup!ZB-(=P~Gcf5SvU7*yvHp*6H*n0D|icRm{)S7;Q(7Nkq vJIHdmzb7{R>Y(+G*cu5!dsbr8?+#k~zwqQ%YHenpM3jo3V4B_krp - - - - CFBundleDevelopmentRegion - English - CFBundleIdentifier - com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - dSYM - CFBundleSignature - ???? - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 deleted file mode 100644 index 3ea9e34d3ba19c88e9fc1c72beeef687d4e233dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12892 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 - - - - CFBundleDevelopmentRegion - English - CFBundleIdentifier - com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - dSYM - CFBundleSignature - ???? - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 deleted file mode 100644 index 0bbec3c68c442f4ff41c7a2658440a06939236fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13536 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 - - - - CFBundleDevelopmentRegion - English - CFBundleIdentifier - com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - dSYM - CFBundleSignature - ???? - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 deleted file mode 100644 index 2d273cb82c5580166166831cf788ca8ab5da86e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12845 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 - - - - CFBundleDevelopmentRegion - English - CFBundleIdentifier - com.apple.xcode.dsym.cp-darwin13.4.0-x86_64 - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - dSYM - CFBundleSignature - ???? - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 b/bin/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/cp-darwin13.4.0-x86_64.dSYM/Contents/Resources/DWARF/cp-darwin13.4.0-x86_64 deleted file mode 100644 index 3ea9e34d3ba19c88e9fc1c72beeef687d4e233dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12892 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 { + activate() { this.subscriptions = new CompositeDisposable(); this.subscriptions.add( - atom.commands.add("atom-text-editor", "Python run: run-f5", run_f5), - atom.commands.add("atom-text-editor", "Python run: run-f6", run_f6) + atom.commands.add('atom-text-editor', 'Python run: run-f5', run_f5), + atom.commands.add('atom-text-editor', 'Python run: run-f6', run_f6) ); }, - deactivate: () => { + deactivate() { this.subscriptions.dispose(); }, config: { f5_command: { - title: "Command of F5", - description: "{file} stands for current file path", - type: "string", - default: "python {file}" + title: 'Command of F5', + description: '`{file}` = `{dir}/{name}{ext}`', + type: 'string', + default: 'python {file}', }, f6_command: { - title: "Command of F6", - description: "{file} stands for current file path", - type: "string", - default: "python {file}" + title: 'Command of F6', + description: 'Same as above', + type: 'string', + default: 'python {file}', }, - disable_notifications: { - title: "Disable success notifications", - description: "Disable notifications while saving and running", - type: "boolean", - default: false + // NOTE: I suppose most of the users don't want to see these + // notifications, this is experimental removal in v1.0.0 + // -- Infinity + // + // disable_notifications: { + // title: 'Disable success notifications', + // description: 'Disable notifications while saving and running', + // type: 'boolean', + // default: false + // }, + // disable_notifications_on_fail: { + // title: 'Disable failure notifications', + // description: 'Disable notifications when extension name does not match', + // type: 'boolean', + // default: false + // }, + extension_filter: { + title: 'Extension filter', + description: 'Leave it empty it accepts all files, or you can try `.py`', + type: 'array', + items: { + type: 'string', + }, + default: [], }, - disable_notifications_on_fail: { - title: "Disable failure notifications", - description: "Disable notifications when extension name does not match", - type: "boolean", - default: false - } }, - subscriptions: null + subscriptions: null, }; function run_f5() { - run(atom.config.get("atom-python-run.f5_command")); + run(atom.config.get('atom-python-run.f5_command')); } function run_f6() { - run(atom.config.get("atom-python-run.f6_command")); + run(atom.config.get('atom-python-run.f6_command')); } function run(command) { - var editor = atom.workspace.getActiveTextEditor(); + let editor = atom.workspace.getActiveTextEditor(); if (!editor) { return; } - var file = editor.buffer.file; + let file = editor.buffer.file; if (!file) { - notification("warning", "You have to create the file first.", true); + atom.notifications.add('warning', 'You have to create the file first.'); return; } - notification("info", "Saving..."); + // information('Saving...'); editor.save(); - var info = path.parse(file.path); - if (info.ext != ".py") { - notification("warning", format("{0} is not a .py file, exit.", [info.base])); + let info = path.parse(file.path); + let filter = atom.config.get('atom-python-run.extension_filter'); + if (filter.length && !filter.includes(info.ext)) { return; } - notification("info", format("Running {0} ...", [info.base])); - var cmds = command.split(" "); - cmds.forEach(function(k, i, a) { - k = new String(k); - a[i] = format(k, { - "file": file.path, - "dir": info.dir, - "name": info.name, - "ext": info.ext, + // information(`Running ${info.base} ...`); + var cmds = command.split(' '); + cmds.forEach((k, i, a) => { + a[i] = k.format({ + 'file': file.path, + 'dir': info.dir, + 'name': info.name, + 'ext': info.ext, }); }); let log = true; let options = { - cwd: info.dir, - detached: true, + cwd: info.dir, + detached: true, }; cmds = [log, options, ...cmds]; let tty = terminal.type.spawns_tty(...cmds); tty.unref(); } -function notification(type, message, always) { - if (type == "info") { - if (always || !atom.config.get("atom-python-run.disable_notifications")) { - atom.notifications.add("info", message); - } - } else if (type == "warning") { - if (always || !atom.config.get("atom-python-run.disable_notifications_on_fail")) { - atom.notifications.add("warning", message); - } - } -} +// function information(message) { +// if (!atom.config.get('atom-python-run.disable_notifications')) { +// atom.notifications.add('info', String(message)); +// } +// } -function format(string, array) { - return string.replace(/{(.*?)}/g, k => array[k.substring(1, k.length - 1)]); -} +String.prototype.format = function(array) { + return this.replace(/{.*?}/g, k => array[k.substring(1, k.length - 1)]); +}; From f440d177dc7f969253a797b9367b103f8005dfc3 Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Sat, 29 Jul 2017 02:53:33 -0400 Subject: [PATCH 22/77] port of cp in C to Python with extra features --- src/__init__.py | 0 src/command.py | 0 src/main.py | 27 ++++++++ src/parser.py | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 src/__init__.py create mode 100644 src/command.py create mode 100644 src/main.py create mode 100644 src/parser.py diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/command.py b/src/command.py new file mode 100644 index 0000000..e69de29 diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..aa30629 --- /dev/null +++ b/src/main.py @@ -0,0 +1,27 @@ +from __future__ import print_function +from sys import argv +from os import system +from parser import Parser + + +if __name__ == '__main__': + parser = Parser(argv) + parser.check_args(argv) + parser.parse_opts() + parser.parse_args() + namespace = parser.get_namespace() + command = list() + command.append(namespace.interpreter) + + for opt in namespace.options: + command.append(opt) + + command.append(namespace.script) + + if namespace.pipe: + command.append(namespace.pipe_symbol) + command.append(namespace.pipe_file) + + print(' '.join(command)) + + r = system(command) diff --git a/src/parser.py b/src/parser.py new file mode 100644 index 0000000..0202663 --- /dev/null +++ b/src/parser.py @@ -0,0 +1,166 @@ +"""parser - handles arbitrary arguments to be executed.""" + +from __future__ import print_function +from sys import argv +from os.path import isfile, expanduser, expandvars + + +class Namespace(object): + pass + + +class SimpleHelpParser(Namespace): + def usage(self): + print('Usage: {} [-h] [-p [filename] [symbol]] interpreter args [args]...'.format(self._progname)) + + def summary(self): + print('{} - handles arbitrary arguments to be executed.\n'.format(self._progname)) + print('Usage\n-----\n\t{} [-h] [-p [filename] [symbol]] interpreter args [args]...'.format(self._progname)) + print( + "\n" + "Options\n" + "-------\n" + "\t-h\t--help\t\t\tprints this help text\n" + "\t-p\t--pipe-to-file\t\tpipes stdout and stderr to the given filename\n" + "\n" + "Parameters\n" + "----------\n" + "\tinterpreter\t\tthe interpreter used to execute script\n" + "\tscript\t\t\tthe file to be executed by interpreter\n" + "\n" + "Symbols\n" + "-------\n" + "\t>\t\ttruncate on stdout\n" + "\t>>\t\tappend on stdout\n" + "\t&>\t\ttruncate on stdout and stderr\n" + "\t&\t\tsynonym for &>\n\n" + "Symbols should always be quoted. Consequences could otherwise occur.\n" + ) + print('Example: {} -p output.log "&" python -i source.py'.format(self._progname)) + + +class SimpleArgParser(Namespace): + def shift(self): + self.index += 1 + + def error(self, taxonomy, message, extendmsg=None): + if not extendmsg: + print('{}: {}'.format(taxonomy, message)) + else: + print('{}: {}: {}'.format(taxonomy, message, extendmsg)) + exit(1) + + def get_arg(self, errmsg=None): + try: + return self.args[self.index] + except IndexError as e: + if errmsg: + print('IndexError: {}: {}'.format(errmsg, e)) + else: + print('IndexError: {}'.format(e)) + exit(1) + + def _set_interpreter(self): + self.shift() + self.namespace.interpreter = self.get_arg('missing interpreter') + + def _set_options(self): + while True: + self.shift() + arg = self.get_arg('missing interpreter argument') + if arg == self.namespace.last_element: + break + self.namespace.options.append(arg) + + def _set_script(self): + # last arg should always be the script! + if not isfile(self.namespace.last_element): + self.error('IOError', 'file does not exist', self.namespace.last_element) + self.namespace.script = expanduser(expandvars(self.namespace.last_element)) + + +class SimpleOptParser(Namespace): + def isvalid(self, argument): + if argument in self._options or argument in self._symbols: + return True + return False + + def _help(self, arg): + if '-h' == arg or '--help' == arg: + self.summary() + exit(1) + + def _set_pipe_flag(self): + self.namespace.pipe = True + + def _set_pipe_file(self): + self.shift() + self.namespace.pipe_file = self.get_arg('no file to pipe to') + + def _set_pipe_symbol(self): + self.shift() + self.namespace.pipe_symbol = self.get_arg('missing pipe symbol') + + def _check_pipe_symbol(self): + if not self.isvalid(self.namespace.pipe_symbol): + self.error('IndexError', 'invalid pipe symbol type', self._symbols) + + def _set_pipe(self, arg): + if '-p' == arg or '--pipe-to-file' == arg: + self._set_pipe_flag() + self._set_pipe_file() + self._set_pipe_symbol() + self._check_pipe_symbol() + + +class Parser(SimpleHelpParser, SimpleOptParser, SimpleArgParser): + def __init__(self, args, progname=None): + self._options = [ + '-h', '--help', + '-p', '--pipe-to-file' + ] + # &> and & are synomynous + self._symbols = ['>', '>>', '&>', '&'] + self._progname = progname or args[0] + self.index = int() + self.args = args[1:] + self.namespace = Namespace() + self.namespace.progname = self._progname + self.namespace.pipe = bool() + self.namespace.pipe_symbol = '>' # default is truncate on stdout (universal) + self.namespace.pipe_file = None + self.namespace.interpreter = None + self.namespace.script = None + self.namespace.options = list() + self.namespace.last_element = args[-1:][0] + + def check_args(self, args): + if not len(args) > 1: + self.usage() + exit(1) + + def get_namespace(self): + return self.namespace + + def parse_opts(self): + arg = self.get_arg() + if self.isvalid(arg): + self._help(arg) + self._set_pipe(arg) + + def parse_args(self): + self._set_interpreter() + self._set_options() + self._set_script() + + +if __name__ == '__main__': + parser = Parser(argv) + parser.check_args(argv) + parser.parse_opts() + parser.parse_args() + + namespace = vars(parser.get_namespace()) + + for key, value in namespace.iteritems(): + print('{} = {}'.format(key, value)) From 008a8de69cc548400a945c5f07653247a3f32ad9 Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Sat, 29 Jul 2017 03:36:29 -0400 Subject: [PATCH 23/77] Fixed importing issue and a bug found in the Parser object --- src/main.py | 8 +++++--- src/{parser.py => parse.py} | 24 ++++++++++++------------ 2 files changed, 17 insertions(+), 15 deletions(-) rename src/{parser.py => parse.py} (93%) diff --git a/src/main.py b/src/main.py index aa30629..049bebf 100644 --- a/src/main.py +++ b/src/main.py @@ -1,7 +1,7 @@ from __future__ import print_function -from sys import argv from os import system -from parser import Parser +from sys import argv +from parse import Parser if __name__ == '__main__': @@ -11,6 +11,7 @@ parser.parse_args() namespace = parser.get_namespace() command = list() + print(namespace.interpreter) command.append(namespace.interpreter) for opt in namespace.options: @@ -23,5 +24,6 @@ command.append(namespace.pipe_file) print(' '.join(command)) + # command = ' '.join(command) - r = system(command) + # r = system(command) diff --git a/src/parser.py b/src/parse.py similarity index 93% rename from src/parser.py rename to src/parse.py index 0202663..21ded79 100644 --- a/src/parser.py +++ b/src/parse.py @@ -1,7 +1,6 @@ """parser - handles arbitrary arguments to be executed.""" from __future__ import print_function -from sys import argv from os.path import isfile, expanduser, expandvars @@ -61,7 +60,8 @@ def get_arg(self, errmsg=None): exit(1) def _set_interpreter(self): - self.shift() + if self.namespace.pipe: + self.shift() self.namespace.interpreter = self.get_arg('missing interpreter') def _set_options(self): @@ -154,13 +154,13 @@ def parse_args(self): self._set_script() -if __name__ == '__main__': - parser = Parser(argv) - parser.check_args(argv) - parser.parse_opts() - parser.parse_args() - - namespace = vars(parser.get_namespace()) - - for key, value in namespace.iteritems(): - print('{} = {}'.format(key, value)) +# if __name__ == '__main__': +# parser = Parser(argv) +# parser.check_args(argv) +# parser.parse_opts() +# parser.parse_args() +# +# namespace = vars(parser.get_namespace()) +# +# for key, value in namespace.iteritems(): +# print('{} = {}'.format(key, value)) From 7f60d8f5eab2d5bbaa946f59c9855d70d6de5e03 Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Mon, 31 Jul 2017 01:51:22 -0400 Subject: [PATCH 24/77] Completed parse.py and critical issues. main.py is now executable. --- src/main.py | 61 ++++++++++++++++++++++++++++++++++--- src/parse.py | 82 +++++++++++++++++++++++++++++++++++++++++++------- src/parse.pyc | Bin 0 -> 8086 bytes 3 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 src/parse.pyc diff --git a/src/main.py b/src/main.py index 049bebf..c6f9fd7 100644 --- a/src/main.py +++ b/src/main.py @@ -1,8 +1,32 @@ +# +# NOTE: +# importing should consider 2to3 and 3to2 implications. +# +# for both backwards and forwards compatability, print_function and clock +# are imported to prevent incompatability issues that may arrise. +# +# clock() is deprecated in python >= 3.3 +# process_time has not been ported back to 2.x +# source: https://stackoverflow.com/questions/85451/python-time-clock-vs-time-time-accuracy#85533 +# +# for all intents and purposes, clock functions very much the same as it does +# in both C and C++ +# source: https://docs.python.org/2.7/library/time.html#time.clock +# +# to avoid naming conflicts, process_time is imported as clock for 3.3 onwards +# +# for measuring snippets and algorithms, the built-in timeit module should be used instead +# source: https://docs.python.org/2/library/timeit.html +# from __future__ import print_function from os import system -from sys import argv +from sys import argv, platform from parse import Parser +try: + from time import process_time as clock +except ImportError: + from time import clock if __name__ == '__main__': parser = Parser(argv) @@ -11,7 +35,6 @@ parser.parse_args() namespace = parser.get_namespace() command = list() - print(namespace.interpreter) command.append(namespace.interpreter) for opt in namespace.options: @@ -23,7 +46,35 @@ command.append(namespace.pipe_symbol) command.append(namespace.pipe_file) - print(' '.join(command)) - # command = ' '.join(command) + # print(' '.join(command)) + command = ' '.join(command) - # r = system(command) + # clock()s value depends on the CPU, Arch type, OS, etc... + # this means the value returned by clock() is indeterminate + + # the common averages for the given systems are + # on win32, this is a microsecond or 1/1000000 of a second + # on linux, this is a millisecond or 1/1000 of a second + t = clock() + r = system(command) + t = clock() - t + if 'win32' == platform: + t /= 1000000. + else: + t /= 1000. + + # for compatability, format is used instead of the % (print) modifier + # https://docs.python.org/2/library/string.html#formatstrings + print( + "\nProcess returned {:d} (0x{:x})\texecution time : {:.6f} s\n", + r, r, t + ) + + if 'win32' == platform: + system('pause') + elif 'darwin' == platform: + system('echo "Close this window to continue..."') + elif 'linux' == platform: + system("printf 'Press [ENTER] to continue...'; read _;") + + exit(r) diff --git a/src/parse.py b/src/parse.py index 21ded79..1d9acc2 100644 --- a/src/parse.py +++ b/src/parse.py @@ -1,6 +1,66 @@ -"""parser - handles arbitrary arguments to be executed.""" +""" +parser - handles arbitrary arguments to be executed. + +NOTE: use the main.py file instead if you want to execute scripts. while parse.py is executable, +it executes as a dummy file and doesn't actually execute anything directly. it simply demonstrates +how parse.py handles the argv variable. + +argv is a list and join could be used in conjunction to create a concatenated string. + +this string could be passed directly to command. it is up to the end user to make sure the +command executes as intended. + +parse.py is mainly designed to be an extremely lightweight check. + +it's rules restrict what can be passed to it while remaining flexible enough to be able +achieve arbitrary execution. + +in example: + +this will always fail... and should always fail. + -> python is the interpreter to be executed by parse.py + -> -c is the option to be passed to python (not parse.py) + -> string is the command to be executed by python (not parse.py) + +the point of the file check is to determine if the file actually exists before attempting to read +it; not to see if it is executable. we also have no intention of writing to it. obviously, a string +is executable, but we want to execute a file; not a string. + + $ python parse.py python -c 'print("hello, world!")' + IOError: file does not exist: print("hello, world!") + + $ python -c "print('Hello, World!')" + bash: !': event not found + + $ python -c 'print("hello, world!")' + hello, world + +parse.py will handle user and variable expansion. globs should never be expanded. +the final argument should always be a source file that can be executed by the given interpreter. + + $ python parse.py node ~/documents/js/hello.js + ...dictionary output here... + +parse.py will handle its own options as long as they are placed before the interpreter argument. +any arguments inbetween the interpreter and the filename are considered options to be passed to the +interpreter itself. + +parse.py also handles redirection. for example, if you want to pipe stdout while appending to a +file, you can do this + + $ python parse.py -p out.log '>>' node ~/documents/js/hello.js + ...dictionary output here... + +for more usage information, use main.py + + $ python main.py -h + ...help output here... + +an example on how to use the Parser object is demonstrated at the end of the file +""" from __future__ import print_function +from sys import argv from os.path import isfile, expanduser, expandvars @@ -154,13 +214,13 @@ def parse_args(self): self._set_script() -# if __name__ == '__main__': -# parser = Parser(argv) -# parser.check_args(argv) -# parser.parse_opts() -# parser.parse_args() -# -# namespace = vars(parser.get_namespace()) -# -# for key, value in namespace.iteritems(): -# print('{} = {}'.format(key, value)) +if __name__ == '__main__': + parser = Parser(argv) + parser.check_args(argv) + parser.parse_opts() + parser.parse_args() + + namespace = vars(parser.get_namespace()) + + for key, value in namespace.iteritems(): + print('{} = {}'.format(key, value)) diff --git a/src/parse.pyc b/src/parse.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd83a6177eab9bb9f5706a61e341325dfba4374a GIT binary patch literal 8086 zcmcgx-F6#S72YGsvZXk2;*p)G)e!~>QcMLWtF;wS+aC&jU&w{ zb4IDPnp`DU+^`l`yadm|BXHN3-~r(K_8E<)wtCTpjO97n=V#8({`vOaN7a8{Z2t4> zr=NEv|69WM@5K}Q8&9MsvX9b}UPT6;3@fr<5s&q%45~7$$$rh%Ycg1pVO{p?GF+DZ zWf`u>{tEh*#5Cj(Z`9?5$a9g;_nXpN7Sq($m7?{6^cvIFX3@GTy$jRU)uQ#H^e&1) z2g+4BtV-`v(RWGAn)KG>QAN@_oOFQWV)X{io;>`M4;vX2_oS5!7GrgVYKls~*1SGq}Dbgs; z!ozXg&7vfB?KQOH{U_U1j;f=G>|r!8Sreo=8DX(8)-6?^U?W)rz3tA>*iolzx~S+M zSGq^3uYfR+x+lTjHoLYfC^9yOVT|{}P-hIoVbU89P<5kpC=2^L6ubRoXm+#y8);&9 zAEe3XAUWCHOU7x}?7kmmAB+!nJ6SSpjV4(?iCgJ7-nD6WS2?vangmorE@`=V-ODcH zYcCp(2Id1Z7=5A)J-hc;G0N>7dJAQPu0mBwRCyZC)#X9?kZJdMV7`>8jvssSRODDC zNnv6V_fp&5!=OLd&XM`G|J5`9i&p<3ijn^X`Aw#vYA3^kWbp8z4@sGHlp2(jul2uR z`EX}v$4QE3Q+cY)4wH1)$y6%gC{2zy+f2UJ3=U`fT?A`0E6 zk2ALVjaI7<{c1ECDj-;8Ke9fX{LGwW7&5|;Q8Y5GENM~r7>SzoZPrW1nGc&IY0{Ji zo%M}>6g@GqKh=|F6S@ltLpO?r^)w}=X*Bp=slkUC_c&jU4!-TC(FnV7-`Fi*;1Z`$ z#6Osn=V>=W6+yx)IIw-+`(k~;6O?A8Dbf>9G z^X^H92%shi;{(4(<7{V;9Qn7lZut&M{8r>!#Tmee+FL-t3y6uq1r%yq!73>{u2_g& zEn_x29u5KPL@Ub`#oNHA>MdX0uAvqXqyr8NXnX;YeYOjw7O|CJ1@rQRmW$TCMm1Xl z;qddF^yn0P5*8KuAAmRwpLIo0xWXD8I1K*<=q1|v3&1K`YDz2%{5)G$2(&!mSxC8x zR^V&xSi$yACB%1uun{rY;T};UihJhd+(g9{Y(F~8enNuh2L#qo@?obshW9``t%|)_ z0z!BQxCiI!<4x^wSg0Ks{X7RjcpgXT5jk($Qu<##qx!0HIFeO&czUFnvSxh8oJ5(^ z9Eww&aMn3V;$%3<$YgkNBDs1G;1le1Xpd$ZwFqTWe*rv8(j6?Q;emK{Z?m%Lb;&vI zx?ou!mC3q#QI$2k#OB^-@yAljD)Oi)X;-{w2q2$&lHQjLVVft%H9@t+)~nh9wxXl( z#n?v9(AbkdmYB*Y>qBbGyWAtWsSKuReY4pynKDq;(kBNrRE=h4LuV2QIJJWifT1VgUpNn)8=}f~l1jmb!%%ayt}xR2O?=TJ=~>ZwWld9!y|Qh1phb=(5qN z&8%RG&}CmX@KI5h^QkHo8p5K4;ywB{U|MszA}asvKV zKZct_=fqUz>ZXEgoSQ`ytIhi*M)=ueWHf>bb}_=K_nV~DN&OVmxrv^-7|=yx-)w3ez{J1!+y~dQG{>$`V7fykl9F%f*Vz z60R`ea9zi!;`ad;FoMRf0uGQt)wV4fmZ<-AclCtEfENk@tz7S#G;&AYqIqWU3Y+OZoj+heGQIHC z;P4A;jX1%E*;4|dkeZj?SvjfUAflpJBhG{#YP zcOA{=2Kc-~%00zvA_s>Y&}0V~y9ScO1%AQyk@ z4S;iS3X}?pl$W~q^8j&5W5i#5LPY)cZ>Z%rQPjNr27vo3?Pge*TKVaHcs*DV1RCV z1WY-x0lDffJnYV>ll5IHul%pEiykRCI5yqPA*Hf|Nvt|Z-JNnr2dYA(jxz;Q6eIaV zr(>4xjv+vVpIq<^t?uQKnP`@X01h`a?AKoi&Vi%KxwqYKrFVIJ>!YYwE_;`~>(!0g X#*K~J8@I0CCUjn8KvG%9)#`r%MWdwg literal 0 HcmV?d00001 From 05f5b00c8e422d27ef56f9f7d83cbb543bcb344b Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Mon, 31 Jul 2017 23:14:31 -0400 Subject: [PATCH 25/77] Made the python version of cp more modular, fixed some bugs, and adding a logging system. --- src/command.py | 0 src/cp.py | 29 ++++++++++++++++++ src/{main.py => cplib.py} | 63 ++++++++++++++++++++++---------------- src/{ => old}/cp.c | 0 src/{ => old}/cplib.c | 0 src/{ => old}/cplib.h | 0 src/parse.py | 26 +++++++++++++--- src/parse.pyc | Bin 8086 -> 0 bytes 8 files changed, 86 insertions(+), 32 deletions(-) delete mode 100644 src/command.py create mode 100644 src/cp.py rename src/{main.py => cplib.py} (62%) rename src/{ => old}/cp.c (100%) rename src/{ => old}/cplib.c (100%) rename src/{ => old}/cplib.h (100%) delete mode 100644 src/parse.pyc diff --git a/src/command.py b/src/command.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/cp.py b/src/cp.py new file mode 100644 index 0000000..39efc06 --- /dev/null +++ b/src/cp.py @@ -0,0 +1,29 @@ + +from __future__ import print_function +from sys import argv +import logging +import cplib + + +if __name__ == '__main__': + # filemode is set to write to avoid an overly sized log file + # if the file is truncated in such a way that is unsuitable, + # just remove the filemode argument from the basicConfig call + logging.basicConfig(filename='cp.log', filemode='w', level=logging.DEBUG) + + cplib.log_os_type() + + namespace = cplib.set_namespace(argv) + command = cplib.set_command(namespace) + + code, time = cplib.set_clock(command) + + # for compatability, str.format() is used instead of the print % modifier + # https://docs.python.org/2/library/string.html#formatstrings + print( + "\nProcess returned {:d} (0x{:x})\texecution time : {:.6f} s\n".format(code, code, time) + ) + + cplib.pause() + + exit(code) diff --git a/src/main.py b/src/cplib.py similarity index 62% rename from src/main.py rename to src/cplib.py index c6f9fd7..fedc32d 100644 --- a/src/main.py +++ b/src/cplib.py @@ -18,63 +18,72 @@ # for measuring snippets and algorithms, the built-in timeit module should be used instead # source: https://docs.python.org/2/library/timeit.html # -from __future__ import print_function from os import system -from sys import argv, platform +from sys import platform +import logging from parse import Parser +# clock()s value depends on the CPU, Arch type, OS, etc... +# this means the value returned by clock() is indeterminate +# the common averages for the given systems are +# on win32, a microsecond or 1/1000000 of a second +# on linux, a millisecond or 1/1000 of a second try: from time import process_time as clock except ImportError: + # deprecated as of v3.3 from time import clock -if __name__ == '__main__': - parser = Parser(argv) - parser.check_args(argv) + +__all__ = ('log_os_type', 'set_namespace', 'set_command', 'set_clock', 'pause') + + +def log_os_type(): + message = 'OS Type: {}'.format(platform) + logging.info(message) + + +def set_namespace(args): + parser = Parser(args) + parser.check_args(args) parser.parse_opts() parser.parse_args() namespace = parser.get_namespace() + for key, value in vars(namespace): + message = 'namespace: key "{}": value "{}"'.format(key, value) + logging.debug(message) + return namespace + + +def set_command(namespace): command = list() command.append(namespace.interpreter) - for opt in namespace.options: command.append(opt) - command.append(namespace.script) - if namespace.pipe: command.append(namespace.pipe_symbol) command.append(namespace.pipe_file) - - # print(' '.join(command)) command = ' '.join(command) + message = 'command: {}'.format(command) + logging.debug(message) + return command - # clock()s value depends on the CPU, Arch type, OS, etc... - # this means the value returned by clock() is indeterminate - # the common averages for the given systems are - # on win32, this is a microsecond or 1/1000000 of a second - # on linux, this is a millisecond or 1/1000 of a second +def set_clock(command): t = clock() r = system(command) t = clock() - t - if 'win32' == platform: - t /= 1000000. - else: - t /= 1000. + return r, t - # for compatability, format is used instead of the % (print) modifier - # https://docs.python.org/2/library/string.html#formatstrings - print( - "\nProcess returned {:d} (0x{:x})\texecution time : {:.6f} s\n", - r, r, t - ) +def pause(): if 'win32' == platform: system('pause') elif 'darwin' == platform: system('echo "Close this window to continue..."') elif 'linux' == platform: system("printf 'Press [ENTER] to continue...'; read _;") - - exit(r) + else: + message = 'Unknown OS Type: {}'.format(platform) + logging.info(message) diff --git a/src/cp.c b/src/old/cp.c similarity index 100% rename from src/cp.c rename to src/old/cp.c diff --git a/src/cplib.c b/src/old/cplib.c similarity index 100% rename from src/cplib.c rename to src/old/cplib.c diff --git a/src/cplib.h b/src/old/cplib.h similarity index 100% rename from src/cplib.h rename to src/old/cplib.h diff --git a/src/parse.py b/src/parse.py index 1d9acc2..1cad542 100644 --- a/src/parse.py +++ b/src/parse.py @@ -62,6 +62,10 @@ from __future__ import print_function from sys import argv from os.path import isfile, expanduser, expandvars +import logging + + +__all__ = ("Parser") class Namespace(object): @@ -101,22 +105,31 @@ def summary(self): class SimpleArgParser(Namespace): def shift(self): self.index += 1 + message = 'shift(): index is now {}'.format(self.index) + logging.debug(message) def error(self, taxonomy, message, extendmsg=None): if not extendmsg: - print('{}: {}'.format(taxonomy, message)) + message = '{}: {}'.format(taxonomy, message) else: - print('{}: {}: {}'.format(taxonomy, message, extendmsg)) + message = '{}: {}: {}'.format(taxonomy, message, extendmsg) + print(message) + logging.error(message) exit(1) def get_arg(self, errmsg=None): try: - return self.args[self.index] + arg = self.args[self.index] + message = 'get_arg(): argument is {}'.format(arg) + logging.debug(message) + return arg except IndexError as e: if errmsg: - print('IndexError: {}: {}'.format(errmsg, e)) + message = 'IndexError: {}: {}'.format(errmsg, e) else: - print('IndexError: {}'.format(e)) + message = 'IndexError: {}'.format(e) + print(message) + logging.error(message) exit(1) def _set_interpreter(self): @@ -152,6 +165,7 @@ def _help(self, arg): def _set_pipe_flag(self): self.namespace.pipe = True + logging.debug('namespace: has pipe') def _set_pipe_file(self): self.shift() @@ -215,6 +229,8 @@ def parse_args(self): if __name__ == '__main__': + logging.basicConfig(filename='testing-cp.log', filemode='w', level=logging.DEBUG) + parser = Parser(argv) parser.check_args(argv) parser.parse_opts() diff --git a/src/parse.pyc b/src/parse.pyc deleted file mode 100644 index cd83a6177eab9bb9f5706a61e341325dfba4374a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8086 zcmcgx-F6#S72YGsvZXk2;*p)G)e!~>QcMLWtF;wS+aC&jU&w{ zb4IDPnp`DU+^`l`yadm|BXHN3-~r(K_8E<)wtCTpjO97n=V#8({`vOaN7a8{Z2t4> zr=NEv|69WM@5K}Q8&9MsvX9b}UPT6;3@fr<5s&q%45~7$$$rh%Ycg1pVO{p?GF+DZ zWf`u>{tEh*#5Cj(Z`9?5$a9g;_nXpN7Sq($m7?{6^cvIFX3@GTy$jRU)uQ#H^e&1) z2g+4BtV-`v(RWGAn)KG>QAN@_oOFQWV)X{io;>`M4;vX2_oS5!7GrgVYKls~*1SGq}Dbgs; z!ozXg&7vfB?KQOH{U_U1j;f=G>|r!8Sreo=8DX(8)-6?^U?W)rz3tA>*iolzx~S+M zSGq^3uYfR+x+lTjHoLYfC^9yOVT|{}P-hIoVbU89P<5kpC=2^L6ubRoXm+#y8);&9 zAEe3XAUWCHOU7x}?7kmmAB+!nJ6SSpjV4(?iCgJ7-nD6WS2?vangmorE@`=V-ODcH zYcCp(2Id1Z7=5A)J-hc;G0N>7dJAQPu0mBwRCyZC)#X9?kZJdMV7`>8jvssSRODDC zNnv6V_fp&5!=OLd&XM`G|J5`9i&p<3ijn^X`Aw#vYA3^kWbp8z4@sGHlp2(jul2uR z`EX}v$4QE3Q+cY)4wH1)$y6%gC{2zy+f2UJ3=U`fT?A`0E6 zk2ALVjaI7<{c1ECDj-;8Ke9fX{LGwW7&5|;Q8Y5GENM~r7>SzoZPrW1nGc&IY0{Ji zo%M}>6g@GqKh=|F6S@ltLpO?r^)w}=X*Bp=slkUC_c&jU4!-TC(FnV7-`Fi*;1Z`$ z#6Osn=V>=W6+yx)IIw-+`(k~;6O?A8Dbf>9G z^X^H92%shi;{(4(<7{V;9Qn7lZut&M{8r>!#Tmee+FL-t3y6uq1r%yq!73>{u2_g& zEn_x29u5KPL@Ub`#oNHA>MdX0uAvqXqyr8NXnX;YeYOjw7O|CJ1@rQRmW$TCMm1Xl z;qddF^yn0P5*8KuAAmRwpLIo0xWXD8I1K*<=q1|v3&1K`YDz2%{5)G$2(&!mSxC8x zR^V&xSi$yACB%1uun{rY;T};UihJhd+(g9{Y(F~8enNuh2L#qo@?obshW9``t%|)_ z0z!BQxCiI!<4x^wSg0Ks{X7RjcpgXT5jk($Qu<##qx!0HIFeO&czUFnvSxh8oJ5(^ z9Eww&aMn3V;$%3<$YgkNBDs1G;1le1Xpd$ZwFqTWe*rv8(j6?Q;emK{Z?m%Lb;&vI zx?ou!mC3q#QI$2k#OB^-@yAljD)Oi)X;-{w2q2$&lHQjLVVft%H9@t+)~nh9wxXl( z#n?v9(AbkdmYB*Y>qBbGyWAtWsSKuReY4pynKDq;(kBNrRE=h4LuV2QIJJWifT1VgUpNn)8=}f~l1jmb!%%ayt}xR2O?=TJ=~>ZwWld9!y|Qh1phb=(5qN z&8%RG&}CmX@KI5h^QkHo8p5K4;ywB{U|MszA}asvKV zKZct_=fqUz>ZXEgoSQ`ytIhi*M)=ueWHf>bb}_=K_nV~DN&OVmxrv^-7|=yx-)w3ez{J1!+y~dQG{>$`V7fykl9F%f*Vz z60R`ea9zi!;`ad;FoMRf0uGQt)wV4fmZ<-AclCtEfENk@tz7S#G;&AYqIqWU3Y+OZoj+heGQIHC z;P4A;jX1%E*;4|dkeZj?SvjfUAflpJBhG{#YP zcOA{=2Kc-~%00zvA_s>Y&}0V~y9ScO1%AQyk@ z4S;iS3X}?pl$W~q^8j&5W5i#5LPY)cZ>Z%rQPjNr27vo3?Pge*TKVaHcs*DV1RCV z1WY-x0lDffJnYV>ll5IHul%pEiykRCI5yqPA*Hf|Nvt|Z-JNnr2dYA(jxz;Q6eIaV zr(>4xjv+vVpIq<^t?uQKnP`@X01h`a?AKoi&Vi%KxwqYKrFVIJ>!YYwE_;`~>(!0g X#*K~J8@I0CCUjn8KvG%9)#`r%MWdwg From ef6e4a84a9bb4f25286ccb5813fc3bd349484b8a Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Tue, 1 Aug 2017 01:32:10 -0400 Subject: [PATCH 26/77] Cleaned up code base... --- bin/cp.log | 8 --- {src => cp/cp}/__init__.py | 0 {src => cp/cp}/cplib.py | 75 ++++++++++++++++++------ {src => cp/cp}/parse.py | 11 ++-- cp/main.py | 34 +++++++++++ {bin => old/bin}/cp-darwin13.4.0-x86_64 | Bin {bin => old/bin}/cp-linux2.6.32-x86_64 | Bin {bin => old/bin}/cp-win32.exe | Bin {src/old => old/src}/cp.c | 0 {src/old => old/src}/cplib.c | 0 {src/old => old/src}/cplib.h | 0 src/cp.py | 29 --------- 12 files changed, 98 insertions(+), 59 deletions(-) delete mode 100644 bin/cp.log rename {src => cp/cp}/__init__.py (100%) rename {src => cp/cp}/cplib.py (52%) rename {src => cp/cp}/parse.py (95%) create mode 100644 cp/main.py rename {bin => old/bin}/cp-darwin13.4.0-x86_64 (100%) mode change 100755 => 100644 rename {bin => old/bin}/cp-linux2.6.32-x86_64 (100%) mode change 100755 => 100644 rename {bin => old/bin}/cp-win32.exe (100%) rename {src/old => old/src}/cp.c (100%) rename {src/old => old/src}/cplib.c (100%) rename {src/old => old/src}/cplib.h (100%) delete mode 100644 src/cp.py diff --git a/bin/cp.log b/bin/cp.log deleted file mode 100644 index a9ac2d5..0000000 --- a/bin/cp.log +++ /dev/null @@ -1,8 +0,0 @@ -Log: file path and name: 'C:\Users\Infinity\.atom\packages\atom-python-run\bin\cp.log' -Log: file path and name: 'C:\Users\Infinity\Desktop\can-t-stop.py' -Log: cmd: 'python "C:\Users\Infinity\Desktop\can-t-stop.py"' -Log: system type: win32 -Log: cp-executable version: v0.7.3 -Log: argv[0]: 'C:\Users\Infinity\.atom\packages\atom-python-run\bin\cp-win32' -Log: argv[1]: 'python' -Log: argv[2]: 'C:\Users\Infinity\Desktop\can-t-stop.py' diff --git a/src/__init__.py b/cp/cp/__init__.py similarity index 100% rename from src/__init__.py rename to cp/cp/__init__.py diff --git a/src/cplib.py b/cp/cp/cplib.py similarity index 52% rename from src/cplib.py rename to cp/cp/cplib.py index fedc32d..7252f5a 100644 --- a/src/cplib.py +++ b/cp/cp/cplib.py @@ -1,5 +1,5 @@ # -# NOTE: +# NOTE: 2to3 and 3to2 Compatability # importing should consider 2to3 and 3to2 implications. # # for both backwards and forwards compatability, print_function and clock @@ -18,16 +18,38 @@ # for measuring snippets and algorithms, the built-in timeit module should be used instead # source: https://docs.python.org/2/library/timeit.html # -from os import system +# clock()s value depends on the CPU, Arch type, OS, etc... +# this means the value returned by clock() is indeterminate +# +# the common averages for the given systems are +# on win32, a microsecond or 1/1000000 of a second +# on linux, a millisecond or 1/1000 of a second +# +# NOTE: set_logpath() issues +# if logpath becomes an issue on win32 platforms (mainly with special characters in the path), +# this may be remedied by escaping that special character. +# +# special_char = '&' +# escaped_special_char = '\&' +# logpath.split(special_char) +# escaped_special_char.join(logpath) +# +# this may be arbitrary even though the most commonly used symbol may be the ampersand (&). +# i refrained from implementing this as it may cause more issues than the one it would solve. +# +# for example, you could inadvertantly create a seperated path looking like this +# from -> c:\Users\User&Name +# to -> c:\Users\User\&Name +# +# using pythons raw strings may be a work around to this issue, but thats for another time +# +from os import system, environ +from os.path import isdir, dirname from sys import platform import logging from parse import Parser -# clock()s value depends on the CPU, Arch type, OS, etc... -# this means the value returned by clock() is indeterminate -# the common averages for the given systems are -# on win32, a microsecond or 1/1000000 of a second -# on linux, a millisecond or 1/1000 of a second + try: from time import process_time as clock except ImportError: @@ -35,12 +57,25 @@ from time import clock -__all__ = ('log_os_type', 'set_namespace', 'set_command', 'set_clock', 'pause') +__all__ = ( + 'set_logpath', 'set_namespace', 'set_command', + 'set_clock', 'print_clock', + 'pause' +) + +version_info = "v0.0.10" -def log_os_type(): - message = 'OS Type: {}'.format(platform) - logging.info(message) + +def set_logpath(): + if 'win32' == platform: + logpath = "{}\\.atom\\packages\\atom-python-run\\cp.log".format(environ['USERPROFILE']) + else: + logpath = "{}/.atom/packages/atom-python-run/cp.log".format(environ['HOME']) + path = dirname(logpath) + if not isdir(path): + return "cp.log" + return logpath def set_namespace(args): @@ -50,8 +85,7 @@ def set_namespace(args): parser.parse_args() namespace = parser.get_namespace() for key, value in vars(namespace): - message = 'namespace: key "{}": value "{}"'.format(key, value) - logging.debug(message) + logging.debug('namespace: key "%s": value "%s"', key, value) return namespace @@ -65,8 +99,7 @@ def set_command(namespace): command.append(namespace.pipe_symbol) command.append(namespace.pipe_file) command = ' '.join(command) - message = 'command: {}'.format(command) - logging.debug(message) + logging.debug('command: %s', command) return command @@ -74,9 +107,18 @@ def set_clock(command): t = clock() r = system(command) t = clock() - t + logging.debug('return code: %d: elapsed time: %d', r, t) return r, t +def print_clock(code, time): + # for compatability, str.format() is used instead of the print % modifier + # https://docs.python.org/2/library/string.html#formatstrings + print( + "\nProcess returned {:d} (0x{:x})\texecution time : {:.6f} s\n".format(code, code, time) + ) + + def pause(): if 'win32' == platform: system('pause') @@ -85,5 +127,4 @@ def pause(): elif 'linux' == platform: system("printf 'Press [ENTER] to continue...'; read _;") else: - message = 'Unknown OS Type: {}'.format(platform) - logging.info(message) + logging.info('Unknown OS Type: %s', platform) diff --git a/src/parse.py b/cp/cp/parse.py similarity index 95% rename from src/parse.py rename to cp/cp/parse.py index 1cad542..fe9712e 100644 --- a/src/parse.py +++ b/cp/cp/parse.py @@ -105,8 +105,7 @@ def summary(self): class SimpleArgParser(Namespace): def shift(self): self.index += 1 - message = 'shift(): index is now {}'.format(self.index) - logging.debug(message) + logging.debug('shift(): index is now %s', self.index) def error(self, taxonomy, message, extendmsg=None): if not extendmsg: @@ -120,8 +119,7 @@ def error(self, taxonomy, message, extendmsg=None): def get_arg(self, errmsg=None): try: arg = self.args[self.index] - message = 'get_arg(): argument is {}'.format(arg) - logging.debug(message) + logging.debug('get_arg(): argument is %s', arg) return arg except IndexError as e: if errmsg: @@ -165,7 +163,6 @@ def _help(self, arg): def _set_pipe_flag(self): self.namespace.pipe = True - logging.debug('namespace: has pipe') def _set_pipe_file(self): self.shift() @@ -209,6 +206,8 @@ def __init__(self, args, progname=None): self.namespace.last_element = args[-1:][0] def check_args(self, args): + for index, arg in enumerate(args): + logging.debug('check_args(): index %d: arg: %s', index, arg) if not len(args) > 1: self.usage() exit(1) @@ -221,11 +220,13 @@ def parse_opts(self): if self.isvalid(arg): self._help(arg) self._set_pipe(arg) + logging.debug('namespace: has pipe: %d', self.namespace.pipe) def parse_args(self): self._set_interpreter() self._set_options() self._set_script() + logging.debug('namespace: script: %s', self.namespace.script) if __name__ == '__main__': diff --git a/cp/main.py b/cp/main.py new file mode 100644 index 0000000..1dd0b6d --- /dev/null +++ b/cp/main.py @@ -0,0 +1,34 @@ +# NOTE: basicConfig is only called once +# +# NOTE: basicConfig()s filemode parameter +# filemode is set to write to avoid an overly sized log file. +# +# if the file is truncated in such a way that is unsuitable, +# just remove the filemode argument from the basicConfig call. +# +from __future__ import print_function +from sys import argv, platform +from logging import basicConfig, DEBUG, info +from cp import cplib + + +if __name__ == '__main__': + logpath = cplib.set_logpath() + + basicConfig(filename=logpath, filemode='w', level=DEBUG, format='%(levelname)s: %(message)s') + + info('OS Type: %s', platform) + + info('Version Info: %s', cplib.version_info) + + namespace = cplib.set_namespace(argv) + + command = cplib.set_command(namespace) + + code, time = cplib.set_clock(command) + + cplib.print_clock(code, time) + + cplib.pause() + + exit(code) diff --git a/bin/cp-darwin13.4.0-x86_64 b/old/bin/cp-darwin13.4.0-x86_64 old mode 100755 new mode 100644 similarity index 100% rename from bin/cp-darwin13.4.0-x86_64 rename to old/bin/cp-darwin13.4.0-x86_64 diff --git a/bin/cp-linux2.6.32-x86_64 b/old/bin/cp-linux2.6.32-x86_64 old mode 100755 new mode 100644 similarity index 100% rename from bin/cp-linux2.6.32-x86_64 rename to old/bin/cp-linux2.6.32-x86_64 diff --git a/bin/cp-win32.exe b/old/bin/cp-win32.exe similarity index 100% rename from bin/cp-win32.exe rename to old/bin/cp-win32.exe diff --git a/src/old/cp.c b/old/src/cp.c similarity index 100% rename from src/old/cp.c rename to old/src/cp.c diff --git a/src/old/cplib.c b/old/src/cplib.c similarity index 100% rename from src/old/cplib.c rename to old/src/cplib.c diff --git a/src/old/cplib.h b/old/src/cplib.h similarity index 100% rename from src/old/cplib.h rename to old/src/cplib.h diff --git a/src/cp.py b/src/cp.py deleted file mode 100644 index 39efc06..0000000 --- a/src/cp.py +++ /dev/null @@ -1,29 +0,0 @@ - -from __future__ import print_function -from sys import argv -import logging -import cplib - - -if __name__ == '__main__': - # filemode is set to write to avoid an overly sized log file - # if the file is truncated in such a way that is unsuitable, - # just remove the filemode argument from the basicConfig call - logging.basicConfig(filename='cp.log', filemode='w', level=logging.DEBUG) - - cplib.log_os_type() - - namespace = cplib.set_namespace(argv) - command = cplib.set_command(namespace) - - code, time = cplib.set_clock(command) - - # for compatability, str.format() is used instead of the print % modifier - # https://docs.python.org/2/library/string.html#formatstrings - print( - "\nProcess returned {:d} (0x{:x})\texecution time : {:.6f} s\n".format(code, code, time) - ) - - cplib.pause() - - exit(code) From 3894f03a78521af92d132f7741567cd9b641706c Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Tue, 1 Aug 2017 02:16:50 -0400 Subject: [PATCH 27/77] Fixed minor bugs and cleaned up code base. cp now works in python making it truly portable! --- cp/cp/cplib.py | 21 ++++++++++++--------- cp/cp/parse.py | 9 +++++++-- cp/main.py | 11 ++++++++--- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/cp/cp/cplib.py b/cp/cp/cplib.py index 7252f5a..5252c5d 100644 --- a/cp/cp/cplib.py +++ b/cp/cp/cplib.py @@ -43,6 +43,9 @@ # # using pythons raw strings may be a work around to this issue, but thats for another time # +# NOTE: Determine platform Type +# https://docs.python.org/2/library/sys.html#sys.platform +# from os import system, environ from os.path import isdir, dirname from sys import platform @@ -58,7 +61,7 @@ __all__ = ( - 'set_logpath', 'set_namespace', 'set_command', + 'set_log_path', 'set_namespace', 'set_command', 'set_clock', 'print_clock', 'pause' ) @@ -67,15 +70,15 @@ version_info = "v0.0.10" -def set_logpath(): +def set_log_path(): if 'win32' == platform: - logpath = "{}\\.atom\\packages\\atom-python-run\\cp.log".format(environ['USERPROFILE']) + log = "{}\\.atom\\packages\\atom-python-run\\cp.log".format(environ['USERPROFILE']) else: - logpath = "{}/.atom/packages/atom-python-run/cp.log".format(environ['HOME']) - path = dirname(logpath) + log = "{}/.atom/packages/atom-python-run/cp.log".format(environ['HOME']) + path = dirname(log) if not isdir(path): return "cp.log" - return logpath + return log def set_namespace(args): @@ -84,7 +87,7 @@ def set_namespace(args): parser.parse_opts() parser.parse_args() namespace = parser.get_namespace() - for key, value in vars(namespace): + for key, value in vars(namespace).items(): logging.debug('namespace: key "%s": value "%s"', key, value) return namespace @@ -107,7 +110,7 @@ def set_clock(command): t = clock() r = system(command) t = clock() - t - logging.debug('return code: %d: elapsed time: %d', r, t) + logging.info('return code: %d: elapsed time: %.6f', r, t) return r, t @@ -124,7 +127,7 @@ def pause(): system('pause') elif 'darwin' == platform: system('echo "Close this window to continue..."') - elif 'linux' == platform: + elif 'linux' == platform[:-1]: system("printf 'Press [ENTER] to continue...'; read _;") else: logging.info('Unknown OS Type: %s', platform) diff --git a/cp/cp/parse.py b/cp/cp/parse.py index fe9712e..67efd81 100644 --- a/cp/cp/parse.py +++ b/cp/cp/parse.py @@ -119,7 +119,7 @@ def error(self, taxonomy, message, extendmsg=None): def get_arg(self, errmsg=None): try: arg = self.args[self.index] - logging.debug('get_arg(): argument is %s', arg) + logging.debug('get_arg(): current argument: %s', arg) return arg except IndexError as e: if errmsg: @@ -230,7 +230,12 @@ def parse_args(self): if __name__ == '__main__': - logging.basicConfig(filename='testing-cp.log', filemode='w', level=logging.DEBUG) + logging.basicConfig( + filename='testing-cp.log', + filemode='w', + level=logging.DEBUG, + format='%(levelname)s: %(message)s' + ) parser = Parser(argv) parser.check_args(argv) diff --git a/cp/main.py b/cp/main.py index 1dd0b6d..aa8c34f 100644 --- a/cp/main.py +++ b/cp/main.py @@ -13,9 +13,14 @@ if __name__ == '__main__': - logpath = cplib.set_logpath() - - basicConfig(filename=logpath, filemode='w', level=DEBUG, format='%(levelname)s: %(message)s') + logpath = cplib.set_log_path() + + basicConfig( + filename=logpath, + filemode='w', + level=DEBUG, + format='%(levelname)s: %(message)s' + ) info('OS Type: %s', platform) From 4826e006f44abbaf62ac0afd96b62e64e18adc76 Mon Sep 17 00:00:00 2001 From: Austin Berrio Date: Tue, 1 Aug 2017 23:01:31 -0400 Subject: [PATCH 28/77] Ported cp to python with extra features. Updated and Refactored terminal. --- cp/cp/cplib.py | 4 +- cp/cp/parse.py | 4 +- cp/main.py | 19 ++++ lib/atom-python-run.js | 42 ++++----- lib/terminal.js | 196 ++++++++++------------------------------- package.json | 2 +- 6 files changed, 91 insertions(+), 176 deletions(-) diff --git a/cp/cp/cplib.py b/cp/cp/cplib.py index 5252c5d..18f9648 100644 --- a/cp/cp/cplib.py +++ b/cp/cp/cplib.py @@ -38,8 +38,8 @@ # i refrained from implementing this as it may cause more issues than the one it would solve. # # for example, you could inadvertantly create a seperated path looking like this -# from -> c:\Users\User&Name -# to -> c:\Users\User\&Name +# from -> c:\\Users\\User&Name +# to -> c:\\Users\\User\\&Name # # using pythons raw strings may be a work around to this issue, but thats for another time # diff --git a/cp/cp/parse.py b/cp/cp/parse.py index 67efd81..d16d31d 100644 --- a/cp/cp/parse.py +++ b/cp/cp/parse.py @@ -51,9 +51,9 @@ $ python parse.py -p out.log '>>' node ~/documents/js/hello.js ...dictionary output here... -for more usage information, use main.py +for more usage information, do - $ python main.py -h + $ python parse.py -h ...help output here... an example on how to use the Parser object is demonstrated at the end of the file diff --git a/cp/main.py b/cp/main.py index aa8c34f..1976a00 100644 --- a/cp/main.py +++ b/cp/main.py @@ -1,3 +1,21 @@ +# Copyright (C) 2017 xovertheyearsx@gmail.com +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# +# # NOTE: basicConfig is only called once # # NOTE: basicConfig()s filemode parameter @@ -5,6 +23,7 @@ # # if the file is truncated in such a way that is unsuitable, # just remove the filemode argument from the basicConfig call. +# by default, filemode is already set to append. # from __future__ import print_function from sys import argv, platform diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index 4391d34..fd901fc 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -1,3 +1,25 @@ +/* + NOTE: win32 and linux only! + NOTE: do not modify the has_a_tty properties on mac os x! + + terminal.type.spawns_tty executes these properties when called. + each property can me modified according to your personal needs. + + modify the default shell + terminal.type.has_a_tty.shell = "shell_name_here"; + + modify the options passed to shell + terminal.type.has_a_tty.option = "--options-go-here"; + + modify the the command + default (win32 only) + if ("win32" === process.platform) terminal.type.has_a_tty.command = "start"; + default (darwin only) + if ("darwin" === process.platform) { + const stringify = terminal.type.has_a_tty.command; + terminal.type.has_a_tty.command = stringify(...args); + } +*/ /* global atom */ 'use strict'; @@ -5,26 +27,6 @@ const CompositeDisposable = require('atom').CompositeDisposable; const path = require('path'); let terminal = require('./terminal'); -// NOTE: win32 and linux only! -// NOTE: do not modify the has_a_tty properties on mac os x! -// -// terminal.type.spawns_tty executes these properties when called. -// each property can me modified according to your personal needs. -// -// modify the default shell -// terminal.type.has_a_tty.shell = "shell_name_here"; -// -// modify the options passed to shell -// terminal.type.has_a_tty.option = "--options-go-here"; -// -// modify the the command -// default (win32 only) -// if ("win32" === process.platform) terminal.type.has_a_tty.command = "start"; -// default (darwin only) -// if ("darwin" === process.platform) { -// const stringify = terminal.type.has_a_tty.command; -// terminal.type.has_a_tty.command = stringify(...args); -// } module.exports = { activate() { diff --git a/lib/terminal.js b/lib/terminal.js index 130a483..7f53f76 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -18,114 +18,6 @@ - Type() creates an instance that refers to the local machines graphical tty - (terminal, shell, prompt, etc...). - - The global value returned by this module is an instance created from Type - exports.type = new Type(); - - Assuming exports.type is called as - let terminal = require('/path/to/terminal'); - - terminal.type is an object that consists of 2 main properties. the rest - is handled by the constructor so you don't have to worry about calling - a new process for each system type. - - The constructor consists of 5 methods and 1 property object. - - NOTE: get_linux_terminal, set_linux_terminal, and has_a_tty are all handled - internally by the constructor and should not require any modifications. - - NOTE: if you want to modify the shell, options, or command properties, you - can do so with the has_a_tty property. - - terminal.type.get_linux_terminal() - returns the name of a linux compatible shell - - terminal.type.set_linux_terminal() - returns an object containing the name of the default shell and - an option to execute the following arguments - - terminal.type.has_a_tty() - returns an object representing the commands the shell should execute - - terminal.type.spawns_tty() - spawns a graphical tty and returns - - terminal.type.has_a_log() - outputs executed properties to built-in console. this is typically - executed each time a tty is spawned with terminal.type.spawns_tty(). - NOTE: there is a speed cost for executing this and should only be - handled by the constructor. - - terminal.type.has_a_tty - a property constructed by has_a_tty(). This property object is passed - to spawns_tty() internally and can be configured by modifying its - raw string properties - - the way terminal.type.has_a_tty is set is system dependent. - If you're on a Windows or Mac OS X machine, you'll have 3 properties. - If you're on a Linux machince, you'll have 2 properties. - - terminal.type - terminal.type.spawns_tty(...[arguments], {options}) - a basic wrapper for nodes child_process.spawn() - https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options - - returns a based on the [arguments] and {options} - parameters. [arguments] should be passed as ...array and {options} - should be passed as {object}. [arguments] can be passed as seperate - primitive string based arguments as well. - - for example, - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator - - const terminal = require('./terminal'); - - var tty = terminal.type.spawns_tty( - { - cwd: undefined, - detached: true, - }, - "python", - "-c", - "print('Hello, World!');input('...enter to continue...')" - ); - - or with the spread operator - - var commands = [ - "python", "-c", - "print('Hello, World!');input('...enter to continue...')" - ]; - - var tty = terminal.type.spawns_tty( - { - cwd: undefined, - detached: true, - }, - ...commands - ); - - terminal.type.has_a_tty - this property has 2 - 3 primitive string based properties depending - on your system. - - terminal.type.has_a_tty.shell - the graphical tty executable (or the shell it self) - - terminal.type.has_a_tty.option - the options to be passed to the graphical tty executable - - terminal.type.has_a_tty.command - For win32 and darwin only. Usually Windows or Mac OS X will need - a special argument in order to execute a 3rd party executable. - - for win32 (Windows) - cmd /c start file-to.exe - for darwin (Mac OS X) - osascript -e 'tell app "Terminal" to do script "/path/to/executable"' - terminal.type.has_a_tty.shell compatible shells listed by system type @@ -146,7 +38,7 @@ const spawn = require('child_process').spawn; -let get_linux_terminal = function () { +let get_linux_shell = function () { switch (process.env.GDMSESSION) { // if session is using gtk case 'ubuntu': @@ -200,15 +92,33 @@ let get_linux_option = function (shell) { } }; +let Get_Linux_Terminal = function () { + let name = get_linux_shell(); + this.name = name; + this.option = get_linux_option(name); +} + let LinuxTTY = function () { // linux has a mass variety of environments and a variety of ways // for determining those environments. best to go for the base // environments by depending on the most common builtin shells instead. // https://askubuntu.com/questions/72549/how-to-determine-which-window-manager-is-running - let terminal = get_linux_terminal(); - this.shell = terminal; - this.option = get_linux_option(terminal); - this.command = `${process.env.HOME}/.atom/packages/atom-python-run/bin/cp-linux2.6.32-x86_64`; + let terminal = new Get_Linux_Terminal(); + this.shell = terminal.name; + this.option = [terminal.option]; + this.call = 'python'; + this.script = `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; +}; + +let Win32TTY = function () { + // windows xp and up can be gaurenteed to have the cmd.exe shell. + // %comspec% should default to %windir%\system32\cmd.exe unless + // otherwise modified by the end user. + // https://en.wikipedia.org/wiki/Environment_variable#Windows + this.shell = process.env.COMSPEC; + this.option = ['/c', 'start']; + this.call = 'python'; + this.script = `${process.env.HOME}\\.atom\\packages\\atom-python-run\\cp\\main.py`; }; let DarwinTTY = function () { @@ -220,7 +130,7 @@ let DarwinTTY = function () { this.shell = 'osascript'; this.option = '-e'; this.command = function (...args) { - let path = `${process.env.HOME}/.atom/packages/atom-python-run/bin/cp-darwin13.4.0-x86_64`; + let path = `python ${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; for (let token of args) { path += ` ${token}`; } @@ -228,16 +138,6 @@ let DarwinTTY = function () { }; }; -let Win32TTY = function () { - // windows xp and up can be gaurenteed to have the cmd.exe shell. - // %comspec% should default to %windir%\system32\cmd.exe unless - // otherwise modified by the end user. - // https://en.wikipedia.org/wiki/Environment_variable#Windows - this.shell = process.env.COMSPEC; - this.option = '/c'; - this.command = `start`; -}; - let has_a_tty = function () { switch (process.platform) { case 'linux': @@ -258,7 +158,8 @@ let has_a_log = function (tty, options, ...args) { if ("darwin" === process.platform) { console.log(`\tcommand: ${this.has_a_tty.command(...args)}\n`); } else { - console.log(`\tcommand: ${this.has_a_tty.command}\n`); + console.log(`\tcall: ${this.has_a_tty.call}\n`); + console.log(`\tscript: ${this.has_a_tty.script}\n`); } console.log( "spawn_tty:\n" + @@ -299,43 +200,36 @@ let spawns_tty = function (log, options, ...args) { let tty; options = set_up_default(options); switch (process.platform) { + // NOTE: Linux OS Types + // this just works regardless of how the arguments are passed. + // as long as everything is recieved in the right way by the + // cp-linux executable, it should just work. + // NOTE: Win32 OS Types + // win32 has some weird path based bug from when spawn() spawns a tty + // to the point where it calls and attempts to pass the path to the + // cp-win32 executable. the command and path to the executable must + // remain as seperate arguments to spawn. theoretically, command does not + // have to be "start". it can just be the cp-win32 executable and should + // spawn a new shell regardless. that would require command to hold the + // cp-win32 path and the cp-win32 path to be removed from this block. + // NOTE: Darwin OS Types + // darwin should never be modified or configured in any way. doing + // so risks breaking the required tool chain. + // NOTE: Darwin has a weird bug that causes multiple windows to spawn. case 'linux': - // this just works regardless of how the arguments are passed. - // as long as everything is recieved in the right way by the - // cp-linux executable, it should just work. - tty = spawn( - this.has_a_tty.shell, - [ - this.has_a_tty.option, - this.has_a_tty.command, - ...args, - ], - options - ) - break; case 'win32': - // win32 has some weird path based bug from when spawn() spawns a tty - // to the point where it calls and attempts to pass the path to the - // cp-win32 executable. the command and path to the executable must - // remain as seperate arguments to spawn. theoretically, command does not - // have to be "start". it can just be the cp-win32 executable and should - // spawn a new shell regardless. that would require command to hold the - // cp-win32 path and the cp-win32 path to be removed from this block. tty = spawn( this.has_a_tty.shell, [ - this.has_a_tty.option, - this.has_a_tty.command, - `${process.env.USERPROFILE}\\.atom\\packages\\atom-python-run\\bin\\cp-win32`, + ...this.has_a_tty.option, + this.has_a_tty.call, + this.has_a_tty.script, ...args, ], options ) break; case 'darwin': - // darwin should never be modified or configured in any way. doing - // so risks breaking the required tool chain. - // NOTE: there is a weird bug that causes multiple windows to spawn. tty = spawn( this.has_a_tty.shell, [ diff --git a/package.json b/package.json index a6c6134..cd21934 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.7.3", + "version": "0.7.6", "description": "Run a python source file.", "keywords": [ "python" From f415a1897f7e64591395a445fd145998e1f9a682 Mon Sep 17 00:00:00 2001 From: Austin Berrio Date: Tue, 1 Aug 2017 23:40:31 -0400 Subject: [PATCH 29/77] Updated to reflect currect changes, updates, and fixes. --- CHANGELOG.md | 14 +++++++++++++- README.md | 26 +++++++++++++++++++------- cp/main.py | 2 +- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f536ca..c92fe83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,22 @@ +## 0.7.6 +* Updated and refactored terminal.js to function with python version of cp + +## 0.7.5 +* Ported cp from C to Python +* Added new features to cp + +## 0.7.4 +* Made cross-platform compatible +* Ported cp from C++ to C +* Added a niche library for cp + ## 0.7.3 * `{dir}` `{name}` `{ext}` is now supported. You can use `python.exe "{dir}\{name}{ext}"`, which is equal to `python.exe "{file}"`. ## 0.7.2 * Trying to fix bug: cannot find file in some environments -## 0.7.0 (skipped due to apm bug) and 0.7.1 +## 0.7.0 (skipped due to apm bug) and 0.7.1 * Fix bug: multiple consoles * New feature: add command setting with f5 and f6 diff --git a/README.md b/README.md index 58ec897..17f7fa8 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,39 @@ -# atom-python-run package +# atom-python-run package [![GitHub issues](https://img.shields.io/github/issues/foreshadow/atom-python-run.svg?style=plastic)](https://github.com/foreshadow/atom-python-run/issues) [![GitHub stars](https://img.shields.io/github/stars/foreshadow/atom-python-run.svg?style=plastic)](https://github.com/foreshadow/atom-python-run/stargazers) [![GitHub forks](https://img.shields.io/github/forks/foreshadow/atom-python-run.svg?style=plastic)](https://github.com/foreshadow/atom-python-run/network) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=plastic)](https://raw.githubusercontent.com/foreshadow/atom-python-run/master/LICENSE.md) -Run your python source file (.py) using `F5` or `F6`! +Run your python (.py) source file using `F5` or `F6`! ![](https://cloud.githubusercontent.com/assets/2712675/18710388/9a665ed8-8037-11e6-803a-35e4555e89d0.jpg) +# Supports +- Python 2 and 3 + - Note: if you have problems executing, you can install a global version of latest `python2.7.x` (even if you have `python3.x.x` installed). Please report any `python3` issues if you want to avoid installing a global `python2` version. + +- Cross Platform Compatible + - Runs on Windows, Mac OS X, and Linux + +- True Arbitrary Execution + - Global python is the default interpreter + - Execute using any interpreter + - Pass options to the given interpreter # Prerequisite -- Windows -- Add the directory of python.exe to ```PATH```. +- Add the directory of `python` (or the intended interpreter) to ```PATH```. + +- Filter file extensions (unset by default) (optional) # Method of use -1. Open a .py file. +1. Open a source file. 2. Hit `F5` or `F6` to run. - - **It will save the file in current editor immediately without a confirmation, be aware.** + - **It will save the file in current editor immediately without a confirmation, be aware.** # Features -- Using `python.exe` +- Using `python` - Almost the same console with python IDLE, which provides syntax error and runtime error messages. - CodeBlocks debug console style - Shows return value and execution time diff --git a/cp/main.py b/cp/main.py index 1976a00..3db4d37 100644 --- a/cp/main.py +++ b/cp/main.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017 xovertheyearsx@gmail.com +# Copyright (C) 2017 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From e3b4e41107bb9a17421688f07fc2314d9178729d Mon Sep 17 00:00:00 2001 From: Austin Berrio Date: Tue, 1 Aug 2017 23:41:39 -0400 Subject: [PATCH 30/77] Deprecated C/C++ version of cp executable. --- old/bin/cp-darwin13.4.0-x86_64 | Bin 15888 -> 0 bytes old/bin/cp-linux2.6.32-x86_64 | Bin 19520 -> 0 bytes old/bin/cp-win32.exe | Bin 73184 -> 0 bytes old/src/cp.c | 74 ------------------ old/src/cplib.c | 137 --------------------------------- old/src/cplib.h | 42 ---------- 6 files changed, 253 deletions(-) delete mode 100644 old/bin/cp-darwin13.4.0-x86_64 delete mode 100644 old/bin/cp-linux2.6.32-x86_64 delete mode 100644 old/bin/cp-win32.exe delete mode 100644 old/src/cp.c delete mode 100644 old/src/cplib.c delete mode 100644 old/src/cplib.h diff --git a/old/bin/cp-darwin13.4.0-x86_64 b/old/bin/cp-darwin13.4.0-x86_64 deleted file mode 100644 index 484695c23aa74a58e70ce3dfad11d7d324b454e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15888 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 diff --git a/old/bin/cp-linux2.6.32-x86_64 b/old/bin/cp-linux2.6.32-x86_64 deleted file mode 100644 index fa72596ccf8434be38bdb3003506cfda22050cdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19520 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 diff --git a/old/src/cp.c b/old/src/cp.c deleted file mode 100644 index 2c975dc..0000000 --- a/old/src/cp.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - compiled as - gcc -g -std=c99 -Wall cp.c cplib.c -o cp-os-version - - Windows - notes: using mingw (Minature GNU for Windows) toolchain - target: win32 - compiler: gcc version 5.3.0 (GCC) - file: PE32 executable (console) Intel 80386, for MS Windows - symbols: not stripped - NOTE: this cp.exe version should not require a local dll. - if it does, it requires the MSVCRT.DLL - - Linux - notes: using gcc toolchain - target: x86_64-linux-gnu - compiler: gcc version 5.4.0 20160609 - file: ELF 64-bit LSB executable, x86-64, dynamically linked, for GNU/Linux 2.6.32 - symbols: not stripped - - Mac OS X - notes: using clang toolchain - target: x86_64-apple-darwin13.4.0 - compiler: Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) - file: Mach-O 64-bit x86_64 executable - symbols: not stripped -*/ -#include "cplib.h" - -extern const bool true; -extern const bool false; - -int main(int argc, char *argv[]) -{ - // program to execute - const char exe[] = "python"; - // dont execute unless arguments are present - if (3 > argc) { - fprintf(stderr, "Usage: %s %s [source-file]", argv[0], exe); - return 2; // invalid arguments - } - // the logs (absolute) path and filename - const char * logfile = make_logpath(); - // the python source file name - const char * filename = make_filename(argc, argv); - // concatenate param tokens to command - // and init the command buffer - const char * cmd = make_command(filename, argv); - // keep the log file from becoming needlessly large in size - remove(logfile); - // start logging data - log_file_path(logfile, logfile); // log the file path and name for log - log_file_path(logfile, filename); // log the path to the python src file - log_cmd_type(logfile, cmd); // log the command to be executed - log_system_type(logfile); // log the systems information - log_cp_version(logfile, "v0.7.3"); // cp version information - log_arg_tokens(logfile, argc, argv); // log all the tokens - // any version of python (and only python) should execute - if (strncmp(exe, argv[1], strlen(exe))) { - log_exe_type(logfile, exe); - return 4; // invalid command type - } - // attempt to read the source file before executing it - if (false == is_readable_file(filename)) { - log_file_read(logfile, filename); - return 8; // failed to read source file - } - // time the execution - int r = execute(cmd); - // pause screen according to os type - prompt(); - // return the child process exit code - return r; -} diff --git a/old/src/cplib.c b/old/src/cplib.c deleted file mode 100644 index 9daffa8..0000000 --- a/old/src/cplib.c +++ /dev/null @@ -1,137 +0,0 @@ -#include "cplib.h" - -const bool true = 1; -const bool false = 0; - -void logs(const char * path, char * string) { - FILE * file = fopen(path, "a"); - if (NULL == file) - { - fprintf( - stderr, - "Error: in logs() function...\npath: '%s'\nstring: '%s'\n", - path, string - ); - perror("Error"); - exit(1); - } - fputs(string, file); - fclose(file); -} - -void log_cp_version(const char * path, const char * version) { - char string[BUFSIZ]; - sprintf(string, "Log: cp-executable version: %s\n", version); - logs(path, string); -} - -void log_exe_type(const char * path, const char * exe) { - char string[BUFSIZ]; - sprintf(string, "Error: exe type: %s programs only =p\n", exe); - logs(path, string); -} - -void log_arg_tokens(const char * path, int argc, char ** argv) { - char string[BUFSIZ]; - for (int index = 0; index < argc; index++) { - sprintf(string, "Log: argv[%i]: '%s'\n", index, argv[index]); - logs(path, string); - } -} - -void log_file_path(const char * path, const char * filename) { - char string[BUFSIZ]; - sprintf(string, "Log: file path and name: '%s'\n", filename); - logs(path, string); -} - -void log_cmd_type(const char * path, const char * cmd) { - char string[BUFSIZ]; - sprintf(string, "Log: cmd: '%s'\n", cmd); - logs(path, string); -} - -void log_file_read(const char * path, const char * filename) { - char string[BUFSIZ]; - sprintf(string, "Error: file: '%s' is not readable.\n", filename); - logs(path, string); -} - -void log_system_type(const char * path) { -#ifdef _WIN32 - logs(path, "Log: system type: win32\n"); -#elif __APPLE__ - logs(path, "Log: system type: darwin\n"); -#elif __linux__ - logs(path, "Log: system type: linux\n"); -#elif __unix__ - logs(path, "Log: system type: unix\n"); -#else - logs(path, "Log: system type: unknown\n"); -#endif -} - -void prompt(void) { -#ifdef _WIN32 - system("pause"); /*"Press any key to continue.\n"*/ -#elif __APPLE__ - system("echo \"Close this window to continue...\""); -#else - system("printf 'Press [ENTER] to continue...'; read _;"); -#endif -} - -bool is_readable_file(const char * filename) { - FILE * file = fopen(filename, "r"); - if (NULL == file) { - perror("Error"); - return false; - } - fclose(file); - return true; -} - -char * make_logpath(void) { - char * logpath = malloc(BUFSIZ); - #ifdef _WIN32 - sprintf(logpath, "%s\\.atom\\packages\\atom-python-run\\bin\\cp.log", getenv("USERPROFILE")); - #else - sprintf(logpath, "%s/.atom/packages/atom-python-run/bin/cp.log", getenv("HOME")); - #endif - return logpath; -} - -char * make_filename(int argc, char **argv) { - char * filename = malloc(BUFSIZ); - memset(filename, '\0', BUFSIZ); - for (int i = 2; i < argc; i++) { - strcat(filename, argv[i]); - if ((argc - 1) != i) { - strcat(filename, " "); - } - } - return filename; -} - -char * make_command(const char * filename, char ** argv) { - char * cmd = malloc(BUFFER); - memset(cmd, '\0', BUFFER); - strcat(cmd, argv[1]); - strcat(cmd, " "); - strcat(cmd, "\""); - strcat(cmd, filename); - strcat(cmd, "\""); - return cmd; -} - -int execute(const char * command) { - int t = clock(); - int r = system(command); // origin command - t = clock() - t; - // output results to user - printf( - "\nProcess returned %d (0x%X)\texecution time : %.3f s\n", - r, r, t / 1000. - ); - return r; -} diff --git a/old/src/cplib.h b/old/src/cplib.h deleted file mode 100644 index 7fc16ee..0000000 --- a/old/src/cplib.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef cplib -# define cplib - -#include -#include -#include -#include - -# ifndef BUFFER -# define BUFFER 4096 -# endif - -typedef _Bool bool; - -void prompt(void); - -bool is_readable_file(const char * filename); - -char * make_filename(int argc, char **argv); - -char * make_command(const char * filename, char ** argv); - -char * make_logpath(void); - -int execute(const char * command); - -void logs(const char * path, char * string); - -void log_cp_version(const char * path, const char * version); - -void log_system_type(const char * path); - -void log_exe_type(const char * path, const char * exe); - -void log_arg_tokens(const char * path, int argc, char ** argv); - -void log_file_path(const char * path, const char * filename); - -void log_cmd_type(const char * path, const char * cmd); - -void log_file_read(const char * path, const char * filename); -#endif From 0a5ce48cf72cbce4f07d9fa10479ed9118877521 Mon Sep 17 00:00:00 2001 From: Austin Berrio Date: Wed, 2 Aug 2017 00:06:23 -0400 Subject: [PATCH 31/77] Updated and refactored readme to reflect current version. --- README.md | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 17f7fa8..28283b7 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,15 @@ Run your python (.py) source file using `F5` or `F6`! ![](https://cloud.githubusercontent.com/assets/2712675/18710388/9a665ed8-8037-11e6-803a-35e4555e89d0.jpg) +# Prerequisite + +- Add the directory of `python` (or the intended interpreter) to ```PATH```. + +- Filter file extensions (unset by default) (optional) + # Supports - Python 2 and 3 - - Note: if you have problems executing, you can install a global version of latest `python2.7.x` (even if you have `python3.x.x` installed). Please report any `python3` issues if you want to avoid installing a global `python2` version. + - Note: If you have problems executing, you can install a global version of latest `python2.7.x` (even if you have `python3.x.x` installed). Please report any `python3` issues if you want to avoid installing a global `python2` version. - Cross Platform Compatible - Runs on Windows, Mac OS X, and Linux @@ -19,12 +25,6 @@ Run your python (.py) source file using `F5` or `F6`! - Execute using any interpreter - Pass options to the given interpreter -# Prerequisite - -- Add the directory of `python` (or the intended interpreter) to ```PATH```. - -- Filter file extensions (unset by default) (optional) - # Method of use 1. Open a source file. @@ -40,6 +40,32 @@ Run your python (.py) source file using `F5` or `F6`! - It is a rough time based on real time rather than CPU kernel time or CPU user time - New an issue if you have any idea of new features. +# Issues +- The new version of atom-python-run fully supports logging. + - Please post these logs with along with issue you're experiencing. + - Note that there are two logs in case the latter fails. The first log is the console log and the second log is created by the cp main.py executable. + +- How to access Atoms Console Log + - Windows/Linux + - Ctrl + Shift + I + - Mac OS X + - Cmd + Alt + I + - Copy and paste the console output along with your post. + +- How to access `cp`'s built-in log + - Windows + 1. Open file explorer + 2. Click the location bar (where the file path usually is) + 3. Type in %userprofile%\.atom\packages\atom-python-run + 4. Locate, Open, and Copy the contents of the cp.log file along with your post. + - Mac OS X/Linux + 1. Open a terminal window. + `$ cat ~/.atom/packages/atom-python-run/cp.log` + 2. Copy and paste the contents along with your post. + - NOTE: If the `cp.log` file is missing or empty, please note that this was case in your post. + +- Detailed issues are well presented issues. This will help us locate your issue. + ![A screenshot of your package](https://f.cloud.github.com/assets/69169/2290250/c35d867a-a017-11e3-86be-cd7c5bf3ff9b.gif) This is a package for Atom From c13c2aced7232852a1fdc2f5b870a1c72a8a3c38 Mon Sep 17 00:00:00 2001 From: Austin Berrio Date: Wed, 2 Aug 2017 00:13:29 -0400 Subject: [PATCH 32/77] Updated and refactored readme to reflect current version. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 28283b7..6f626b4 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Run your python (.py) source file using `F5` or `F6`! 1. Open a terminal window. `$ cat ~/.atom/packages/atom-python-run/cp.log` 2. Copy and paste the contents along with your post. - - NOTE: If the `cp.log` file is missing or empty, please note that this was case in your post. + - NOTE: If the `cp.log` file is missing, empty, or inaccurate, please note that this was case in your post. - Detailed issues are well presented issues. This will help us locate your issue. From 405146ac22d425b79aeea864fabfec9cf2e3029b Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Wed, 2 Aug 2017 00:50:15 -0400 Subject: [PATCH 33/77] Fixed win32 bug. this.has_a_tty.script resulted in undefined user path. --- lib/terminal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/terminal.js b/lib/terminal.js index 7f53f76..52dac30 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -118,7 +118,7 @@ let Win32TTY = function () { this.shell = process.env.COMSPEC; this.option = ['/c', 'start']; this.call = 'python'; - this.script = `${process.env.HOME}\\.atom\\packages\\atom-python-run\\cp\\main.py`; + this.script = `${process.env.USERPROFILE}\\.atom\\packages\\atom-python-run\\cp\\main.py`; }; let DarwinTTY = function () { From 790be9654a92332f0f42a4dbc4d56361dcabec02 Mon Sep 17 00:00:00 2001 From: Austin Date: Wed, 2 Aug 2017 02:10:54 -0400 Subject: [PATCH 34/77] fixed windows path in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f626b4..2f5aed2 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Run your python (.py) source file using `F5` or `F6`! - Windows 1. Open file explorer 2. Click the location bar (where the file path usually is) - 3. Type in %userprofile%\.atom\packages\atom-python-run + 3. Type in `%userprofile%\.atom\packages\atom-python-run` 4. Locate, Open, and Copy the contents of the cp.log file along with your post. - Mac OS X/Linux 1. Open a terminal window. From 5afd263895e5eef799ddd0d86c5f4f612fcd1957 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Sat, 5 Aug 2017 18:35:15 +0800 Subject: [PATCH 35/77] Fixed errors when path contains space or `&' --- .gitignore | 3 +++ CHANGELOG.md | 7 ++--- LICENSE.md | 2 +- README.md | 49 ++++++++++++++++++++++++---------- cp/cp/cplib.py | 8 +++--- lib/atom-python-run.js | 24 +++++++++++++---- lib/terminal.js | 60 +++++++++++++++++++----------------------- package.json | 2 +- 8 files changed, 92 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index 280a0a8..70983ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ build test +*.pyc +*.log + .DS_Store npm-debug.log node_modules diff --git a/CHANGELOG.md b/CHANGELOG.md index c92fe83..6da81ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,8 @@ -## 0.7.6 +## 0.8.0 +* cp is now optional * Updated and refactored terminal.js to function with python version of cp - -## 0.7.5 * Ported cp from C to Python * Added new features to cp - -## 0.7.4 * Made cross-platform compatible * Ported cp from C++ to C * Added a niche library for cp diff --git a/LICENSE.md b/LICENSE.md index 5f3f537..4f6c6ed 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2016 +Copyright (c) 2016 foreshadow (a.k.a. Infinity) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 2f5aed2..6fb244a 100644 --- a/README.md +++ b/README.md @@ -7,38 +7,57 @@ Run your python (.py) source file using `F5` or `F6`! ![](https://cloud.githubusercontent.com/assets/2712675/18710388/9a665ed8-8037-11e6-803a-35e4555e89d0.jpg) + # Prerequisite - Add the directory of `python` (or the intended interpreter) to ```PATH```. - Filter file extensions (unset by default) (optional) -# Supports -- Python 2 and 3 - - Note: If you have problems executing, you can install a global version of latest `python2.7.x` (even if you have `python3.x.x` installed). Please report any `python3` issues if you want to avoid installing a global `python2` version. - -- Cross Platform Compatible - - Runs on Windows, Mac OS X, and Linux - -- True Arbitrary Execution - - Global python is the default interpreter - - Execute using any interpreter - - Pass options to the given interpreter - # Method of use 1. Open a source file. 2. Hit `F5` or `F6` to run. - **It will save the file in current editor immediately without a confirmation, be aware.** + # Features - Using `python` - Almost the same console with python IDLE, which provides syntax error and runtime error messages. - CodeBlocks debug console style - Shows return value and execution time - - It is a rough time based on real time rather than CPU kernel time or CPU user time -- New an issue if you have any idea of new features. + - It is a rough time based on real time rather than CPU kernel / user time + +# Settings + +- Extension filter + - It accepts all extension by default (empty value), you can change it into `.py` + - or an array `.py, .something-else` + +- Command + - You can hack it using these variant, or + - `{file}` = `{dir}/{name}{ext}` + - Run with interactive mode, or + - `python -i {file}` + - Run with idle, and you may need the next setting. + - `pythonw C:\python27\Lib\idlelib\idle.pyw -r {file}` + +- Exit immediately + - You can disable the default cp wrapper if you don't want it. + +# Compatibility + +- Cross Platform Compatible + - Runs on Windows, Mac OS X, and Linux + +- True Arbitrary Execution + - Global python is the default interpreter + - Execute using any interpreter + - Pass options to the given interpreter + +- Python 2 and 3 + - Note: If you have problems executing, you can install a global version of latest `python2.7.x` (even if you have `python3.x.x` installed). Please report any `python3` issues if you want to avoid installing a global `python2` version. # Issues - The new version of atom-python-run fully supports logging. @@ -66,6 +85,8 @@ Run your python (.py) source file using `F5` or `F6`! - Detailed issues are well presented issues. This will help us locate your issue. +- New an issue if you have any idea of new features. + ![A screenshot of your package](https://f.cloud.github.com/assets/69169/2290250/c35d867a-a017-11e3-86be-cd7c5bf3ff9b.gif) This is a package for Atom diff --git a/cp/cp/cplib.py b/cp/cp/cplib.py index 18f9648..751bf50 100644 --- a/cp/cp/cplib.py +++ b/cp/cp/cplib.py @@ -48,6 +48,7 @@ # from os import system, environ from os.path import isdir, dirname +from subprocess import call from sys import platform import logging from parse import Parser @@ -101,16 +102,15 @@ def set_command(namespace): if namespace.pipe: command.append(namespace.pipe_symbol) command.append(namespace.pipe_file) - command = ' '.join(command) logging.debug('command: %s', command) return command def set_clock(command): t = clock() - r = system(command) + r = call(command) t = clock() - t - logging.info('return code: %d: elapsed time: %.6f', r, t) + logging.info('return code: %d, elapsed time: %.6f', r, t) return r, t @@ -118,7 +118,7 @@ def print_clock(code, time): # for compatability, str.format() is used instead of the print % modifier # https://docs.python.org/2/library/string.html#formatstrings print( - "\nProcess returned {:d} (0x{:x})\texecution time : {:.6f} s\n".format(code, code, time) + "\nProcess returned {:d} (0x{:x}) execution time : {:.3f} s".format(code, code, time) ) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index fd901fc..b16ae5b 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -77,19 +77,33 @@ module.exports = { }, default: [], }, + f5_no_cp: { + title: 'Exit immediately when program ends (F5)', + description: 'Don\'t show time elasped and don\'t pause.', + type: 'boolean', + default: false, + }, + f6_no_cp: { + title: 'Exit immediately when program ends (F6)', + description: 'Don\'t show time elasped and don\'t pause.', + type: 'boolean', + default: false, + }, }, subscriptions: null, }; function run_f5() { - run(atom.config.get('atom-python-run.f5_command')); + run(atom.config.get('atom-python-run.f5_command'), + atom.config.get('atom-python-run.f5_no_cp')); } function run_f6() { - run(atom.config.get('atom-python-run.f6_command')); + run(atom.config.get('atom-python-run.f6_command'), + atom.config.get('atom-python-run.f6_no_cp')); } -function run(command) { +function run(command, nocp) { let editor = atom.workspace.getActiveTextEditor(); if (!editor) { return; @@ -107,7 +121,7 @@ function run(command) { return; } // information(`Running ${info.base} ...`); - var cmds = command.split(' '); + let cmds = command.split(' '); // TODO: double quote? cmds.forEach((k, i, a) => { a[i] = k.format({ 'file': file.path, @@ -121,7 +135,7 @@ function run(command) { cwd: info.dir, detached: true, }; - cmds = [log, options, ...cmds]; + cmds = [log, options, nocp, ...cmds]; let tty = terminal.type.spawns_tty(...cmds); tty.unref(); } diff --git a/lib/terminal.js b/lib/terminal.js index 52dac30..f3ec775 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -38,7 +38,7 @@ const spawn = require('child_process').spawn; -let get_linux_shell = function () { +let get_linux_shell = function() { switch (process.env.GDMSESSION) { // if session is using gtk case 'ubuntu': @@ -49,10 +49,10 @@ let get_linux_shell = function () { case 'gnome-fallback': case 'cinnamon': return "gnome-terminal"; - // xfce session has its own terminal, xfce is gtk compatible + // xfce session has its own terminal, xfce is gtk compatible case 'xfce': return "xfce4-terminal"; - // if session is using qt, kde and lxde are qt compatible + // if session is using qt, kde and lxde are qt compatible case 'kde-plasma': return "konsole"; case 'Lubuntu': @@ -70,14 +70,14 @@ let get_linux_shell = function () { return "konsole"; case 'LXDE': return "lxterminal"; - // if unknown session, default to xterm + // if unknown session, default to xterm default: return "xterm"; } } }; -let get_linux_option = function (shell) { +let get_linux_option = function(shell) { // determine the shells default execution option switch (shell) { case 'gnome-terminal': @@ -92,13 +92,13 @@ let get_linux_option = function (shell) { } }; -let Get_Linux_Terminal = function () { +let Get_Linux_Terminal = function() { let name = get_linux_shell(); this.name = name; this.option = get_linux_option(name); } -let LinuxTTY = function () { +let LinuxTTY = function() { // linux has a mass variety of environments and a variety of ways // for determining those environments. best to go for the base // environments by depending on the most common builtin shells instead. @@ -110,7 +110,7 @@ let LinuxTTY = function () { this.script = `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; }; -let Win32TTY = function () { +let Win32TTY = function() { // windows xp and up can be gaurenteed to have the cmd.exe shell. // %comspec% should default to %windir%\system32\cmd.exe unless // otherwise modified by the end user. @@ -121,7 +121,7 @@ let Win32TTY = function () { this.script = `${process.env.USERPROFILE}\\.atom\\packages\\atom-python-run\\cp\\main.py`; }; -let DarwinTTY = function () { +let DarwinTTY = function() { // NOTE: osascript should always be used! consider your self warned! // the default shell is determined by the Terminal.app settings. // the path method must be called in order to generate a valid @@ -129,7 +129,7 @@ let DarwinTTY = function () { // an empty primitive string otherwise. this.shell = 'osascript'; this.option = '-e'; - this.command = function (...args) { + this.command = function(...args) { let path = `python ${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; for (let token of args) { path += ` ${token}`; @@ -138,7 +138,7 @@ let DarwinTTY = function () { }; }; -let has_a_tty = function () { +let has_a_tty = function() { switch (process.platform) { case 'linux': return new LinuxTTY(); @@ -149,7 +149,7 @@ let has_a_tty = function () { } }; -let has_a_log = function (tty, options, ...args) { +let has_a_log = function(tty, options, ...args) { console.log( `platform: ${process.platform}\n` + `has_a_tty:\n\tshell: ${this.has_a_tty.shell}\n` + @@ -167,26 +167,26 @@ let has_a_log = function (tty, options, ...args) { `\targs: ${[...args]}\n` ); tty.stdout.on('data', (data) => { - console.log(`stdout: ${data}`); + console.log(`stdout: ${data}`); }); tty.stderr.on('data', (data) => { - console.log(`stderr: ${data}`); + console.log(`stderr: ${data}`); }); tty.on('close', (code) => { - console.log(`child process exited with code ${code}`); + console.log(`child process exited with code ${code}`); }); }; -let is_empty = function (object) { - for(var property in object) { - if(object.hasOwnProperty(property)) { +let is_empty = function(object) { + for (var property in object) { + if (object.hasOwnProperty(property)) { return false; } } return JSON.stringify(object) === JSON.stringify({}); } -let set_up_default = function (options) { +let set_up_default = function(options) { if (is_empty(options)) { return { 'cwd': undefined, @@ -196,7 +196,7 @@ let set_up_default = function (options) { return options } -let spawns_tty = function (log, options, ...args) { +let spawns_tty = function(log, options, nocp, ...args) { let tty; options = set_up_default(options); switch (process.platform) { @@ -218,21 +218,15 @@ let spawns_tty = function (log, options, ...args) { // NOTE: Darwin has a weird bug that causes multiple windows to spawn. case 'linux': case 'win32': - tty = spawn( - this.has_a_tty.shell, - [ - ...this.has_a_tty.option, - this.has_a_tty.call, - this.has_a_tty.script, - ...args, - ], - options - ) + let pargs = [this.has_a_tty.option]; + if (nocp !== true) { + pargs = [...pargs, this.has_a_tty.call, this.has_a_tty.script]; + } + tty = spawn(this.has_a_tty.shell, [...pargs, ...args], options); break; case 'darwin': tty = spawn( - this.has_a_tty.shell, - [ + this.has_a_tty.shell, [ this.has_a_tty.option, this.has_a_tty.command(...args), ], @@ -246,7 +240,7 @@ let spawns_tty = function (log, options, ...args) { return tty; }; -let Type = function () { +let Type = function() { this.has_a_tty = has_a_tty(); this.has_a_log = has_a_log; this.spawns_tty = spawns_tty; diff --git a/package.json b/package.json index cd21934..a6c6134 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.7.6", + "version": "0.7.3", "description": "Run a python source file.", "keywords": [ "python" From bbf834b6dba930ea8d838874faad33997163421a Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Mon, 7 Aug 2017 10:31:15 +0800 Subject: [PATCH 36/77] Prepare 0.8.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a6c6134..51c155f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.7.3", + "version": "0.8.0", "description": "Run a python source file.", "keywords": [ "python" From 4ddc928878770a56fbb3ff57dfb8a4a5b60ed968 Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Mon, 7 Aug 2017 04:36:54 -0400 Subject: [PATCH 37/77] refactored and cleaned up terminal for readability. --- cp/cp/cplib.py | 5 +- lib/src/linux.js | 65 ++++++++++++ lib/src/log.js | 43 ++++++++ lib/src/tty.js | 48 +++++++++ lib/terminal.js | 252 ++++++++++++++--------------------------------- 5 files changed, 234 insertions(+), 179 deletions(-) create mode 100644 lib/src/linux.js create mode 100644 lib/src/log.js create mode 100644 lib/src/tty.js diff --git a/cp/cp/cplib.py b/cp/cp/cplib.py index 751bf50..65e540c 100644 --- a/cp/cp/cplib.py +++ b/cp/cp/cplib.py @@ -25,6 +25,9 @@ # on win32, a microsecond or 1/1000000 of a second # on linux, a millisecond or 1/1000 of a second # +# for compatability, str.format() is used instead of the print % modifier +# https://docs.python.org/2/library/string.html#formatstrings +# # NOTE: set_logpath() issues # if logpath becomes an issue on win32 platforms (mainly with special characters in the path), # this may be remedied by escaping that special character. @@ -115,8 +118,6 @@ def set_clock(command): def print_clock(code, time): - # for compatability, str.format() is used instead of the print % modifier - # https://docs.python.org/2/library/string.html#formatstrings print( "\nProcess returned {:d} (0x{:x}) execution time : {:.3f} s".format(code, code, time) ) diff --git a/lib/src/linux.js b/lib/src/linux.js new file mode 100644 index 0000000..a0f6cbf --- /dev/null +++ b/lib/src/linux.js @@ -0,0 +1,65 @@ + +let shell = function () { + switch (process.env.GDMSESSION) { + // if session is using gtk + case 'ubuntu': + case 'ubuntu-2d': + case 'gnome': + case 'gnome-shell': + case 'gnome-classic': + case 'gnome-fallback': + case 'cinnamon': + return "gnome-terminal"; + // xfce session has its own terminal, xfce is gtk compatible + case 'xfce': + return "xfce4-terminal"; + // if session is using qt, kde and lxde are qt compatible + case 'kde-plasma': + return "konsole"; + case 'Lubuntu': + return "lxterminal"; + default: + // attempt to determine desktop session + switch (process.env.XDG_CURRENT_DESKTOP) { + case 'Unity': + case 'GNOME': + case 'X-Cinnamon': + return "gnome-terminal"; + case 'XFCE': + return "xfce4-terminal"; + case 'KDE': + return "konsole"; + case 'LXDE': + return "lxterminal"; + // if unknown session, default to xterm + default: + return "xterm"; + } + } +}; + + +let shell_option = function (shell) { + // determine the shells default execution option + switch (shell) { + case 'gnome-terminal': + case 'xfce4-terminal': + return "-x" + case 'konsole': + case 'lxterminal': + default: + return "-e" + } +}; + + +let Terminal = function (name, option) { + this.name = name; + this.option = option; +}; + + +let name = shell(); +let option = shell_option(name); + +exports.terminal = new Terminal(name, option); diff --git a/lib/src/log.js b/lib/src/log.js new file mode 100644 index 0000000..ced0bcf --- /dev/null +++ b/lib/src/log.js @@ -0,0 +1,43 @@ + +let log_type_info = function (object) { + console.log(`platform: ${process.platform}\n`); + console.log(`has_a_tty:\n\tshell: ${object.has_a_tty.shell}\n`); + console.log(`\toption: ${object.has_a_tty.option}\n`); + console.log(`\tcall: ${object.has_a_tty.call}\n`); + console.log(`\tscript: ${object.has_a_tty.script}\n`); + if ("darwin" === process.platform) { + console.log(`\tcommand: ${object.has_a_tty.command(...args)}\n`); + } +}; + + +let log_param_info = function (options, ...args) { + console.log( + "spawn_tty:\n" + + `\toptions:\n${JSON.stringify(options, null, 4)}\n` + + `\targs: ${[...args]}\n` + ); +}; + + +let log_tty_info = function (tty) { + tty.stdout.on('data', (data) => { + console.log(`stdout: ${data}`); + }); + tty.stderr.on('data', (data) => { + console.log(`stderr: ${data}`); + }); + tty.on('close', (code) => { + console.log(`child process exited with code ${code}`); + }); +}; + + +let has_a_log = function (tty, options, ...args) { + log_type_info(this); + log_param_info(options, ...args); + log_tty_info(tty); +}; + + +exports.has_a_log = has_a_log; diff --git a/lib/src/tty.js b/lib/src/tty.js new file mode 100644 index 0000000..8e159fa --- /dev/null +++ b/lib/src/tty.js @@ -0,0 +1,48 @@ + +const linux = require('./linux'); + + +let LinuxTTY = function () { + this.shell = linux.terminal.name; + this.option = [linux.terminal.option]; + this.call = 'python'; + this.script = `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; +}; + + +let Win32TTY = function () { + this.shell = process.env.COMSPEC; + this.option = ['/c', 'start']; + this.call = 'python'; + this.script = `${process.env.USERPROFILE}\\.atom\\packages\\atom-python-run\\cp\\main.py`; +}; + + +let DarwinTTY = function () { + this.shell = 'osascript'; + this.option = '-e'; + this.call = 'python' + this.script = `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; + this.command = function (...args) { + let path = this.call + " " + this.script; + for (let token of args) { + path += ` ${token}`; + } + return `tell app "Terminal" to do script "${path}"` + }; +}; + + +let has_a_tty = function () { + switch (process.platform) { + case 'linux': + return new LinuxTTY(); + case 'darwin': + return new DarwinTTY(); + case 'win32': + return new Win32TTY(); + } +}; + + +exports.has_a_tty = has_a_tty(); diff --git a/lib/terminal.js b/lib/terminal.js index f3ec775..26eafd3 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -18,6 +18,38 @@ + NOTE: Linux + linux has a mass variety of environments and a variety of ways + for determining those environments. best to go for the base + environments by depending on the most common builtin shells instead. + https://askubuntu.com/questions/72549/how-to-determine-which-window-manager-is-running + + this just works regardless of how the arguments are passed. + as long as everything is recieved in the right way by cp/main.py, it should just work. + + NOTE: Windows + windows xp and up can be gaurenteed to have the cmd.exe shell. + %comspec% should default to %windir%\system32\cmd.exe unless otherwise modified by the + end user. + https://en.wikipedia.org/wiki/Environment_variable#Windows + + win32 has some weird path based bug from when spawn() spawns a tty to the point where it + calls and attempts to pass the path to cp/main.py. the command and path to the executable + must remain as seperate arguments to spawn. theoretically, command does not have to be + "start". it can just be cp/main.py and should spawn a new shell regardless. + + NOTE: Darwin + NOTE: osascript should always be used! consider your self warned! + NOTE: Darwin has a weird bug that causes multiple windows to spawn. + + the default shell is determined by the Terminal.app settings. + the path method must be called in order to generate a valid command every time it is called. + the command property contains an empty primitive string otherwise. + + darwin should never be modified or configured in any way. doing so risks breaking the + required tool chain. + + terminal.type.has_a_tty.shell compatible shells listed by system type @@ -37,213 +69,79 @@ 'use strict'; const spawn = require('child_process').spawn; +const has_a_tty = require('./src/tty').has_a_tty; +const has_a_log = require('./src/log').has_a_log; -let get_linux_shell = function() { - switch (process.env.GDMSESSION) { - // if session is using gtk - case 'ubuntu': - case 'ubuntu-2d': - case 'gnome': - case 'gnome-shell': - case 'gnome-classic': - case 'gnome-fallback': - case 'cinnamon': - return "gnome-terminal"; - // xfce session has its own terminal, xfce is gtk compatible - case 'xfce': - return "xfce4-terminal"; - // if session is using qt, kde and lxde are qt compatible - case 'kde-plasma': - return "konsole"; - case 'Lubuntu': - return "lxterminal"; - default: - // attempt to determine desktop session - switch (process.env.XDG_CURRENT_DESKTOP) { - case 'Unity': - case 'GNOME': - case 'X-Cinnamon': - return "gnome-terminal"; - case 'XFCE': - return "xfce4-terminal"; - case 'KDE': - return "konsole"; - case 'LXDE': - return "lxterminal"; - // if unknown session, default to xterm - default: - return "xterm"; - } - } -}; -let get_linux_option = function(shell) { - // determine the shells default execution option - switch (shell) { - case 'gnome-terminal': - case 'xfce4-terminal': - return "-x" - case 'konsole': - case 'lxterminal': - case 'xterm': - return "-e" - default: - return "-e" +let is_empty = function (object) { + for(var property in object) { + if(object.hasOwnProperty(property)) { + return false; + } } + return JSON.stringify(object) === JSON.stringify({}); }; -let Get_Linux_Terminal = function() { - let name = get_linux_shell(); - this.name = name; - this.option = get_linux_option(name); -} - -let LinuxTTY = function() { - // linux has a mass variety of environments and a variety of ways - // for determining those environments. best to go for the base - // environments by depending on the most common builtin shells instead. - // https://askubuntu.com/questions/72549/how-to-determine-which-window-manager-is-running - let terminal = new Get_Linux_Terminal(); - this.shell = terminal.name; - this.option = [terminal.option]; - this.call = 'python'; - this.script = `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; -}; -let Win32TTY = function() { - // windows xp and up can be gaurenteed to have the cmd.exe shell. - // %comspec% should default to %windir%\system32\cmd.exe unless - // otherwise modified by the end user. - // https://en.wikipedia.org/wiki/Environment_variable#Windows - this.shell = process.env.COMSPEC; - this.option = ['/c', 'start']; - this.call = 'python'; - this.script = `${process.env.USERPROFILE}\\.atom\\packages\\atom-python-run\\cp\\main.py`; +let spawn_default_options = function (options) { + if (is_empty(options)) { + return { + 'cwd': undefined, + 'detached': false, + }; + } + return options; }; -let DarwinTTY = function() { - // NOTE: osascript should always be used! consider your self warned! - // the default shell is determined by the Terminal.app settings. - // the path method must be called in order to generate a valid - // command every time it is called. the command property contains - // an empty primitive string otherwise. - this.shell = 'osascript'; - this.option = '-e'; - this.command = function(...args) { - let path = `python ${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; - for (let token of args) { - path += ` ${token}`; - } - return `tell app "Terminal" to do script "${path}"` - }; -}; -let has_a_tty = function() { - switch (process.platform) { - case 'linux': - return new LinuxTTY(); - case 'darwin': - return new DarwinTTY(); - case 'win32': - return new Win32TTY(); - } +let spawn_win32 = function (object, options, ...args) { + return spawn( + object.has_a_tty.shell, + [ ...object.has_a_tty.option, + object.has_a_tty.call, + object.has_a_tty.script, + ...args + ], + options + ); }; -let has_a_log = function(tty, options, ...args) { - console.log( - `platform: ${process.platform}\n` + - `has_a_tty:\n\tshell: ${this.has_a_tty.shell}\n` + - `\toption: ${this.has_a_tty.option}\n` - ); - if ("darwin" === process.platform) { - console.log(`\tcommand: ${this.has_a_tty.command(...args)}\n`); - } else { - console.log(`\tcall: ${this.has_a_tty.call}\n`); - console.log(`\tscript: ${this.has_a_tty.script}\n`); - } - console.log( - "spawn_tty:\n" + - `\toptions:\n${JSON.stringify(options, null, 4)}\n` + - `\targs: ${[...args]}\n` + +let spawn_darwin = function (object, options, ...args) { + return spawn( + object.has_a_tty.shell, + [ object.has_a_tty.option, + object.has_a_tty.command(...args) + ], + options ); - tty.stdout.on('data', (data) => { - console.log(`stdout: ${data}`); - }); - tty.stderr.on('data', (data) => { - console.log(`stderr: ${data}`); - }); - tty.on('close', (code) => { - console.log(`child process exited with code ${code}`); - }); }; -let is_empty = function(object) { - for (var property in object) { - if (object.hasOwnProperty(property)) { - return false; - } - } - return JSON.stringify(object) === JSON.stringify({}); -} - -let set_up_default = function(options) { - if (is_empty(options)) { - return { - 'cwd': undefined, - 'detached': false, - }; - } - return options -} -let spawns_tty = function(log, options, nocp, ...args) { +let spawns_tty = function (log, options, ...args) { let tty; - options = set_up_default(options); + let opts = spawn_default_options(options); switch (process.platform) { - // NOTE: Linux OS Types - // this just works regardless of how the arguments are passed. - // as long as everything is recieved in the right way by the - // cp-linux executable, it should just work. - // NOTE: Win32 OS Types - // win32 has some weird path based bug from when spawn() spawns a tty - // to the point where it calls and attempts to pass the path to the - // cp-win32 executable. the command and path to the executable must - // remain as seperate arguments to spawn. theoretically, command does not - // have to be "start". it can just be the cp-win32 executable and should - // spawn a new shell regardless. that would require command to hold the - // cp-win32 path and the cp-win32 path to be removed from this block. - // NOTE: Darwin OS Types - // darwin should never be modified or configured in any way. doing - // so risks breaking the required tool chain. - // NOTE: Darwin has a weird bug that causes multiple windows to spawn. case 'linux': case 'win32': - let pargs = [this.has_a_tty.option]; - if (nocp !== true) { - pargs = [...pargs, this.has_a_tty.call, this.has_a_tty.script]; - } - tty = spawn(this.has_a_tty.shell, [...pargs, ...args], options); + tty = spawn_win32(this, opts, ...args); break; case 'darwin': - tty = spawn( - this.has_a_tty.shell, [ - this.has_a_tty.option, - this.has_a_tty.command(...args), - ], - options - ); + tty = spawn_darwin(this, opts, ...args); break; } if (log) { - this.has_a_log(tty, options, ...args); + this.has_a_log(tty, opts, ...args); } return tty; }; -let Type = function() { - this.has_a_tty = has_a_tty(); + +let Type = function () { + this.has_a_tty = has_a_tty; this.has_a_log = has_a_log; this.spawns_tty = spawns_tty; }; + exports.type = new Type(); From 1e15dfc3139fe5b2b57d9b18838e97945ffd5ae1 Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Mon, 7 Aug 2017 18:55:51 -0400 Subject: [PATCH 38/77] hotfix for issue 44 for python 3 global execution --- .gitignore | 2 ++ cp/cp/cplib.py | 51 ++++++++++++++++++++++++++++++++++++-------------- cp/cp/parse.py | 2 +- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 70983ed..8fbe81c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ test *.pyc *.log +__pycache__ + .DS_Store npm-debug.log node_modules diff --git a/cp/cp/cplib.py b/cp/cp/cplib.py index 751bf50..46f00df 100644 --- a/cp/cp/cplib.py +++ b/cp/cp/cplib.py @@ -2,18 +2,39 @@ # NOTE: 2to3 and 3to2 Compatability # importing should consider 2to3 and 3to2 implications. # -# for both backwards and forwards compatability, print_function and clock -# are imported to prevent incompatability issues that may arrise. +# NOTE: clock() is deprecated in python >= 3.3 +# for both backwards and forwards compatability, time and clock are imported to prevent +# incompatability issues that may arrise. # -# clock() is deprecated in python >= 3.3 -# process_time has not been ported back to 2.x -# source: https://stackoverflow.com/questions/85451/python-time-clock-vs-time-time-accuracy#85533 +# process_time has not been ported back to 2.x. I'm also not sure how to implement process_time or +# pref_time correctly. +# +# i decided to opt for time() instead since it is not deprecated in python 2 or 3. time() also +# seems to be slightly more accurate than clock() since it measures time from the epoch +# (Jan 1, 1970, 00:00:00 UTC) in seconds. +# +# >>> from time import time +# >>> from time import clock +# >>> t = time() +# >>> t = time() - t +# >>> t +# 2.4237000942230225 +# >>> c = clock() +# >>> c = clock() - c +# >>> c +# 0.0 +# +# t returns ~ 2.4 seconds, while c returns ~ 0 seconds since the start of the process +# +# sources: +# https://stackoverflow.com/questions/85451/python-time-clock-vs-time-time-accuracy#85533 +# https://docs.python.org/3/library/time.html#time.time # # for all intents and purposes, clock functions very much the same as it does # in both C and C++ # source: https://docs.python.org/2.7/library/time.html#time.clock # -# to avoid naming conflicts, process_time is imported as clock for 3.3 onwards +# to avoid naming conflicts, time is imported as clock for 3.3 onwards # # for measuring snippets and algorithms, the built-in timeit module should be used instead # source: https://docs.python.org/2/library/timeit.html @@ -49,16 +70,16 @@ from os import system, environ from os.path import isdir, dirname from subprocess import call +from time import time from sys import platform import logging -from parse import Parser - try: - from time import process_time as clock + # python 2.x.x + from parse import Parser except ImportError: - # deprecated as of v3.3 - from time import clock + # python 3.x.x + from .parse import Parser __all__ = ( @@ -107,9 +128,11 @@ def set_command(namespace): def set_clock(command): - t = clock() + # should be set to .6f and not .3f since win32 is in microseconds, not milliseconds + # .3f is fine for unix based systems since time is usually in milliseconds + t = time() r = call(command) - t = clock() - t + t = time() - t logging.info('return code: %d, elapsed time: %.6f', r, t) return r, t @@ -118,7 +141,7 @@ def print_clock(code, time): # for compatability, str.format() is used instead of the print % modifier # https://docs.python.org/2/library/string.html#formatstrings print( - "\nProcess returned {:d} (0x{:x}) execution time : {:.3f} s".format(code, code, time) + "\nProcess returned {:d} (0x{:x}) execution time : {:.6f} s".format(code, code, time) ) diff --git a/cp/cp/parse.py b/cp/cp/parse.py index d16d31d..07a7269 100644 --- a/cp/cp/parse.py +++ b/cp/cp/parse.py @@ -244,5 +244,5 @@ def parse_args(self): namespace = vars(parser.get_namespace()) - for key, value in namespace.iteritems(): + for key, value in namespace.items(): print('{} = {}'.format(key, value)) From e563c74e6609154396dabbc61151fd9452682e2e Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Tue, 8 Aug 2017 12:22:58 +0800 Subject: [PATCH 39/77] Update changelog.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6da81ef..3fb121c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.8.1 +* Fixed python3 import error + ## 0.8.0 * cp is now optional * Updated and refactored terminal.js to function with python version of cp From 6024f6a7d431baac70a8103d303122fd9d98178e Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Tue, 8 Aug 2017 12:23:20 +0800 Subject: [PATCH 40/77] Prepare 0.8.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 51c155f..743f4dd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.8.0", + "version": "0.8.1", "description": "Run a python source file.", "keywords": [ "python" From 5429b4f3ddc2680ca831c946dd232696a18a668f Mon Sep 17 00:00:00 2001 From: jargon Date: Sun, 27 Aug 2017 02:33:51 -0400 Subject: [PATCH 41/77] properly patched cp. refactoring terminal; still needs a lot of work. --- cp/cp/cplib.py | 14 ++-- lib/src/linux.js | 151 +++++++++++++++++++++++++-------------- lib/src/log.js | 102 +++++++++++++++++--------- lib/src/spawn_wrapper.js | 15 ++++ lib/src/tty.js | 149 +++++++++++++++++++++++++++++--------- lib/terminal.js | 18 ++--- package.json | 2 +- 7 files changed, 308 insertions(+), 143 deletions(-) create mode 100644 lib/src/spawn_wrapper.js diff --git a/cp/cp/cplib.py b/cp/cp/cplib.py index 65e540c..0d5acfe 100644 --- a/cp/cp/cplib.py +++ b/cp/cp/cplib.py @@ -49,20 +49,14 @@ # NOTE: Determine platform Type # https://docs.python.org/2/library/sys.html#sys.platform # +from __future__ import absolute_import from os import system, environ from os.path import isdir, dirname from subprocess import call +from time import time as clock from sys import platform import logging -from parse import Parser - - -try: - from time import process_time as clock -except ImportError: - # deprecated as of v3.3 - from time import clock - +from .parse import Parser __all__ = ( 'set_log_path', 'set_namespace', 'set_command', @@ -113,7 +107,7 @@ def set_clock(command): t = clock() r = call(command) t = clock() - t - logging.info('return code: %d, elapsed time: %.6f', r, t) + logging.info('return code: %d, execution time: %.6f', r, t) return r, t diff --git a/lib/src/linux.js b/lib/src/linux.js index a0f6cbf..0ee1ef0 100644 --- a/lib/src/linux.js +++ b/lib/src/linux.js @@ -1,65 +1,106 @@ +/* + CREATE A LINUX CONSOLE WINDOW -let shell = function () { - switch (process.env.GDMSESSION) { - // if session is using gtk - case 'ubuntu': - case 'ubuntu-2d': - case 'gnome': - case 'gnome-shell': - case 'gnome-classic': - case 'gnome-fallback': - case 'cinnamon': - return "gnome-terminal"; - // xfce session has its own terminal, xfce is gtk compatible - case 'xfce': - return "xfce4-terminal"; - // if session is using qt, kde and lxde are qt compatible - case 'kde-plasma': - return "konsole"; - case 'Lubuntu': - return "lxterminal"; - default: - // attempt to determine desktop session - switch (process.env.XDG_CURRENT_DESKTOP) { - case 'Unity': - case 'GNOME': - case 'X-Cinnamon': - return "gnome-terminal"; - case 'XFCE': - return "xfce4-terminal"; - case 'KDE': - return "konsole"; - case 'LXDE': - return "lxterminal"; - // if unknown session, default to xterm - default: - return "xterm"; - } + automatically determine the linux os desktop environment. + + create an object based on the current desktop session being used in linux. + + this object contains information for the default shell and the proper execution option to be passed to that shell. + + the methods get_shell() and get_option() can be used instead of the properties shell and option. + + this.shell and this.option are convenience properties. +*/ +'use strict'; + + +class Linux { + constructor() { + this.setUp(); + }; + + get() { + return { + 'shell': this.shell, + 'option': this.option + } } -}; + set(object) { + this.shell = object.shell; + this.option = object.option; + } -let shell_option = function (shell) { - // determine the shells default execution option - switch (shell) { - case 'gnome-terminal': - case 'xfce4-terminal': - return "-x" - case 'konsole': - case 'lxterminal': - default: - return "-e" + setUp() { + set({ + 'shell': this.setupDefaultShell(), + 'option': this.setupDefaultOption(this.shell); + }); } -}; + getSessionTerminal() { + switch (process.env.GDMSESSION) { + case 'ubuntu': + case 'ubuntu-2d': + case 'gnome': + case 'gnome-shell': + case 'gnome-classic': + case 'gnome-fallback': + case 'cinnamon': + return "gnome-terminal"; + case 'xfce': + return "xfce4-terminal"; + case 'kde-plasma': + return "konsole"; + case 'Lubuntu': + return "lxterminal"; + default: + return undefined; + } + } -let Terminal = function (name, option) { - this.name = name; - this.option = option; -}; + getDesktopTerminal() { + switch (process.env.XDG_CURRENT_DESKTOP) { + case 'Unity': + case 'GNOME': + case 'X-Cinnamon': + return "gnome-terminal"; + case 'XFCE': + return "xfce4-terminal"; + case 'KDE': + return "konsole"; + case 'LXDE': + return "lxterminal"; + default: + return "xterm"; + } + } + + setupDefaultShell() { + let shell = this.getSessionTerminal(); + + if (undefined === shell) { + shell = this.getDesktopTerminal(); + } + return shell; + } + + setupDefaultOption(shell) { + // determine the shells default execution option + switch (shell) { + case 'gnome-terminal': + case 'xfce4-terminal': + return "-x"; + case 'konsole': + case 'lxterminal': + case 'xterm': + return "-e"; + default: + return undefined; + } + } +}; -let name = shell(); -let option = shell_option(name); -exports.terminal = new Terminal(name, option); +exports.terminal = new Linux(); diff --git a/lib/src/log.js b/lib/src/log.js index ced0bcf..1889c12 100644 --- a/lib/src/log.js +++ b/lib/src/log.js @@ -1,43 +1,77 @@ +/* + CREATE A LOG OBJECT TO CAPTURE BASIC I/O OPERATIONS -let log_type_info = function (object) { - console.log(`platform: ${process.platform}\n`); - console.log(`has_a_tty:\n\tshell: ${object.has_a_tty.shell}\n`); - console.log(`\toption: ${object.has_a_tty.option}\n`); - console.log(`\tcall: ${object.has_a_tty.call}\n`); - console.log(`\tscript: ${object.has_a_tty.script}\n`); - if ("darwin" === process.platform) { - console.log(`\tcommand: ${object.has_a_tty.command(...args)}\n`); - } -}; + automate the logging process while executing in atom. + + create a Log object that prints I/O operations to standard output. + this object prints all relavent information for inspecting basic I/O operations. -let log_param_info = function (options, ...args) { - console.log( - "spawn_tty:\n" + - `\toptions:\n${JSON.stringify(options, null, 4)}\n` + - `\targs: ${[...args]}\n` - ); -}; + this object should be used ONLY after spawn has executed and a tty instance has been created. +*/ +class Log { + constructor(object) { + if (null == object) { + this.defaultSetUp(); + } else { + this.setUp(object); + } + } -let log_tty_info = function (tty) { - tty.stdout.on('data', (data) => { - console.log(`stdout: ${data}`); - }); - tty.stderr.on('data', (data) => { - console.log(`stderr: ${data}`); - }); - tty.on('close', (code) => { - console.log(`child process exited with code ${code}`); - }); -}; + defaultSetUp() { + this.setUp({ + 'tty': null, + 'options': null, + 'args': null, + 'metadata': null + }); + } + setUp(object) { + this.tty = object.tty; + this.option = object.options; + this.arg = object.args; + this.object = object.metadata; + } -let has_a_log = function (tty, options, ...args) { - log_type_info(this); - log_param_info(options, ...args); - log_tty_info(tty); -}; + logMetaData() { + console.log( + `platform: ${process.platform}\n` + + `has_a_tty:\n` + + `shell: ${this.object.has_a_tty.shell}\n` + + `option: ${this.object.has_a_tty.option}\n` + + `call: ${this.object.has_a_tty.call}\n` + + `script: ${this.object.has_a_tty.script}\n` + + `command: ${this.object.has_a_tty.command(...this.arg)}\n` + ); + } + logParams() { + console.log( + "spawn_tty:\n" + + `\toptions:\n${JSON.stringify(this.option, null, 4)}\n` + + `\targs: ${[...this.arg]}\n` + ); + } + + logTTY(tty) { + this.tty.stdout.on('data', (data) => { + console.log(`stdout: ${data}`); + }); + this.tty.stderr.on('data', (data) => { + console.log(`stderr: ${data}`); + }); + this.tty.on('close', (code) => { + console.log(`child process exited with code ${code}`); + }); + } + + info() { + this.logMetaData(this.object); + this.logParams(this.option, ...this.arg); + this.logTTY(this.tty); + } +} -exports.has_a_log = has_a_log; +exports.log = new Log(); diff --git a/lib/src/spawn_wrapper.js b/lib/src/spawn_wrapper.js new file mode 100644 index 0000000..a01cfdc --- /dev/null +++ b/lib/src/spawn_wrapper.js @@ -0,0 +1,15 @@ + + +class SpawnWrapper { + constructor(object) { + + } +} + +class Type { + constructor (object) { + this.has_a_tty = object.has_a_tty; + this.has_a_log = object.has_a_log; + this.spawn_tty = object.spawn_tty; + } +} diff --git a/lib/src/tty.js b/lib/src/tty.js index 8e159fa..74e6854 100644 --- a/lib/src/tty.js +++ b/lib/src/tty.js @@ -1,48 +1,129 @@ +/* + CREATE A META TERMINAL OBJECT THAT IS DEFINED BY OPERATING SYSTEM TYPE + + automatically create a TTY object. + + create a TTY object based on the current operating system. + + this object contains all the required information for executing a script within a tty instance. + + has_a_tty is the function to utilize in automating the process. +*/ +'use strict'; + const linux = require('./linux'); -let LinuxTTY = function () { - this.shell = linux.terminal.name; - this.option = [linux.terminal.option]; - this.call = 'python'; - this.script = `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; -}; +class TTY { + constructor(object) { + this.setUp(object); + } + object() { + return { + 'shell': this.shell, + 'option': this.option, + 'call': this.call, + 'script': this.script, + 'command': this.command + } + } -let Win32TTY = function () { - this.shell = process.env.COMSPEC; - this.option = ['/c', 'start']; - this.call = 'python'; - this.script = `${process.env.USERPROFILE}\\.atom\\packages\\atom-python-run\\cp\\main.py`; -}; + setBase(object) { + this.shell = object.shell; + this.option = object.option; + this.call = object.call; + } + setPath(object) { + this.script = (function(path) { + if (null == path) { + if ('win32' === process.platform) { + path = `${process.env.USERPROFILE}\\.atom\\packages\\atom-python-run\\cp\\main.py`; + } else { + path = `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; + } + } + return path; + })(object.path); + } -let DarwinTTY = function () { - this.shell = 'osascript'; - this.option = '-e'; - this.call = 'python' - this.script = `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; - this.command = function (...args) { - let path = this.call + " " + this.script; - for (let token of args) { - path += ` ${token}`; + setCommand(object) { + if ('darwin' === process.platform) { + this.command = function (...args) { + let path = this.call + " " + this.script; + for (let token of args) { + path += ` ${token}`; + } + return `tell app "Terminal" to do script "${path}"` + }; + } else { + this.command = null; } - return `tell app "Terminal" to do script "${path}"` - }; -}; + } + + setUp(object) { + this.setBase(object); + this.setPath(object); + this.setCommand(object); + } +} -let has_a_tty = function () { - switch (process.platform) { - case 'linux': - return new LinuxTTY(); - case 'darwin': - return new DarwinTTY(); - case 'win32': - return new Win32TTY(); +class Console { + constructor(path) { + this.has_a_tty = this.setUp(path); + } + + getTTY() { + return this.has_a_tty.object(); + } + + setTTY(object) { + this.has_a_tty.setUp(object); + } + + win32(path) { + return new TTY({ + 'shell': process.env.COMSPEC, + 'option': ['/c', 'start'], + 'call': 'python', + 'path': path + }); + } + + darwin(path) { + return new TTY({ + 'shell': 'osascript', + 'option': ['-e'], + 'call': 'python', + 'path': path + }); + } + + linux(path) { + return new TTY({ + 'shell': linux.terminal.shell, + 'option': [linux.terminal.option], + 'call': 'python', + 'path': path + }); + } + + setUp(path) { + switch (process.platform) { + case 'win32': + return this.win32(path); + case 'darwin': + return this.darwin(path); + case 'linux': + return this.linux(path); + default: + return undefined; + } } -}; +} -exports.has_a_tty = has_a_tty(); +exports.console = new Console(); diff --git a/lib/terminal.js b/lib/terminal.js index 26eafd3..65f41a0 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -18,15 +18,6 @@ - NOTE: Linux - linux has a mass variety of environments and a variety of ways - for determining those environments. best to go for the base - environments by depending on the most common builtin shells instead. - https://askubuntu.com/questions/72549/how-to-determine-which-window-manager-is-running - - this just works regardless of how the arguments are passed. - as long as everything is recieved in the right way by cp/main.py, it should just work. - NOTE: Windows windows xp and up can be gaurenteed to have the cmd.exe shell. %comspec% should default to %windir%\system32\cmd.exe unless otherwise modified by the @@ -38,6 +29,15 @@ must remain as seperate arguments to spawn. theoretically, command does not have to be "start". it can just be cp/main.py and should spawn a new shell regardless. + NOTE: Linux + linux has a mass variety of environments and a variety of ways + for determining those environments. best to go for the base + environments by depending on the most common builtin shells instead. + https://askubuntu.com/questions/72549/how-to-determine-which-window-manager-is-running + + this just works regardless of how the arguments are passed. + as long as everything is recieved in the right way by cp/main.py, it should just work. + NOTE: Darwin NOTE: osascript should always be used! consider your self warned! NOTE: Darwin has a weird bug that causes multiple windows to spawn. diff --git a/package.json b/package.json index 51c155f..743f4dd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.8.0", + "version": "0.8.1", "description": "Run a python source file.", "keywords": [ "python" From 8c27b867bf68abce8c2284dbe30487f04ded9cee Mon Sep 17 00:00:00 2001 From: jargon Date: Tue, 29 Aug 2017 03:39:29 -0400 Subject: [PATCH 42/77] touched up and tested base objects. main objects still need some work. --- lib/src/linux.js | 62 +++-- lib/src/spawn_wrapper.js | 66 ++++++ lib/src/tty.js | 149 +++++++----- lib/terminal.js | 491 ++++++++++++++++++++++++++++++--------- 4 files changed, 577 insertions(+), 191 deletions(-) diff --git a/lib/src/linux.js b/lib/src/linux.js index 0ee1ef0..a9b1901 100644 --- a/lib/src/linux.js +++ b/lib/src/linux.js @@ -14,29 +14,53 @@ 'use strict'; -class Linux { - constructor() { - this.setUp(); - }; +class Base { + constructor(base, ...source){ + this.set(base, ...source) + } get() { - return { - 'shell': this.shell, - 'option': this.option + return this.object; + } + + set(base, ...source) { + if (null == base) { + this.object = new Object(); + } else if (null == source) { + this.object = Object.assign({}, base); + } else { + this.object = Object.assign(base, ...source) } + (function log(object, base, ...source) { + console.log( + `base: ${JSON.stringify(base)}\n` + + `source: ${JSON.stringify(source)}\n` + + `this: ${JSON.stringify(object.object)}\n` + ); + })(this, base, ...source); } - set(object) { - this.shell = object.shell; - this.option = object.option; + isEmpty() { + for(var property in this.object) { + if(this.object.hasOwnProperty(property)) { + return false; + } + } + return JSON.stringify(this.object) === JSON.stringify({}); } +} + - setUp() { - set({ - 'shell': this.setupDefaultShell(), - 'option': this.setupDefaultOption(this.shell); +class LinuxSessionWrapper extends Base { + constructor() { + super(); + let shell = this.setupDefaultShell(); + let option = this.setupDefaultOption(shell); + this.set({ + 'shell': shell, + 'option': option }); - } + }; getSessionTerminal() { switch (process.env.GDMSESSION) { @@ -55,7 +79,7 @@ class Linux { case 'Lubuntu': return "lxterminal"; default: - return undefined; + return null; } } @@ -79,7 +103,7 @@ class Linux { setupDefaultShell() { let shell = this.getSessionTerminal(); - if (undefined === shell) { + if (null === shell) { shell = this.getDesktopTerminal(); } @@ -97,10 +121,10 @@ class Linux { case 'xterm': return "-e"; default: - return undefined; + return null; } } }; -exports.terminal = new Linux(); +exports.session_wrapper = new LinuxSessionWrapper(); diff --git a/lib/src/spawn_wrapper.js b/lib/src/spawn_wrapper.js index a01cfdc..a9ee990 100644 --- a/lib/src/spawn_wrapper.js +++ b/lib/src/spawn_wrapper.js @@ -1,11 +1,77 @@ +const spawn = require('child_process').spawn; +const console = require('./tty').console; +const log = require('./log').log; class SpawnWrapper { constructor(object) { } + + isEmpty(object) { + for(var property in object) { + if(object.hasOwnProperty(property)) { + return false; + } + } + return JSON.stringify(object) === JSON.stringify({}); + } + + defaultOptions(options) { + if (this.isEmpty(options)) { + return { + 'cwd': undefined, + 'detached': false, + }; + } + return options; + } + + spawnWin32(object, options, ...args) { + return spawn( + object.has_a_tty.shell, + [ ...object.has_a_tty.option, + object.has_a_tty.call, + object.has_a_tty.script, + ...args + ], + options + ); + } + + spawnDarwin(object, options, ...args) { + return spawn( + object.has_a_tty.shell, + [ object.has_a_tty.option, + object.has_a_tty.command(...args) + ], + options + ); + } + + spawnWrapper(object, options, ...args) { + switch (process.platform) { + case 'linux': + case 'win32': + return this.spawnWin32(object, options, ...args); + case 'darwin': + return this.spawnDarwin(object, options, ...args); + default: + return null; + } + } + + spawnTTY(log, options, ...args) { + let opts = this.defaultOptions(options); + let tty = spawnWrapper(object, opts, ...args); + if (log) { + this.has_a_log(tty, opts, ...args); + } + return tty; + } } + class Type { constructor (object) { this.has_a_tty = object.has_a_tty; diff --git a/lib/src/tty.js b/lib/src/tty.js index 74e6854..05ffd0f 100644 --- a/lib/src/tty.js +++ b/lib/src/tty.js @@ -12,118 +12,141 @@ 'use strict'; -const linux = require('./linux'); +const linux = require('./linux').session_wrapper; -class TTY { - constructor(object) { - this.setUp(object); +class Base { + constructor(base, ...source){ + this.set(base, ...source) } - object() { - return { - 'shell': this.shell, - 'option': this.option, - 'call': this.call, - 'script': this.script, - 'command': this.command + get() { + return this.object; + } + + set(base, ...source) { + if (null == base) { + this.object = new Object(); + } else if (null == source) { + this.object = Object.assign({}, base); + } else { + this.object = Object.assign(base, ...source) } + (function log(object, base, ...source) { + console.log( + `base: ${JSON.stringify(base)}\n` + + `source: ${JSON.stringify(source)}\n` + + `this: ${JSON.stringify(object.object)}\n` + ); + })(this, base, ...source); } - setBase(object) { - this.shell = object.shell; - this.option = object.option; - this.call = object.call; + isEmpty() { + for(var property in this.object) { + if(this.object.hasOwnProperty(property)) { + return false; + } + } + return JSON.stringify(this.object) === JSON.stringify({}); } +} - setPath(object) { - this.script = (function(path) { - if (null == path) { - if ('win32' === process.platform) { - path = `${process.env.USERPROFILE}\\.atom\\packages\\atom-python-run\\cp\\main.py`; - } else { - path = `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; - } + +class MetaShell extends Base { + constructor(base, ...source) { + super(); + this.setBase(base, ...source); + this.setPath(base); + this.setCommand(); + } + + setBase(base, ...source) { + this.set({ + 'shell': base.shell, + 'option': base.option, + 'call': base.call + }, ...source); + } + + setPath(base) { + function defaultPath() { + if ('win32' === process.platform) { + return `${process.env.USERPROFILE}\\.atom\\packages\\atom-python-run\\cp\\main.py`; } - return path; - })(object.path); + return `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; + } + this.set(this.get(), { + 'script': (function(path) { + if (null == path) { + return defaultPath(); + } + return path; + })(base.script) + }); } - setCommand(object) { - if ('darwin' === process.platform) { - this.command = function (...args) { - let path = this.call + " " + this.script; + setCommand() { + let object = this.get(); + this.set(object, { + 'command': function (...args) { + let path = object.call + " " + object.script; for (let token of args) { path += ` ${token}`; } return `tell app "Terminal" to do script "${path}"` - }; - } else { - this.command = null; - } - } - - setUp(object) { - this.setBase(object); - this.setPath(object); - this.setCommand(object); + } + }); } } -class Console { +class Shell extends Base { constructor(path) { - this.has_a_tty = this.setUp(path); - } - - getTTY() { - return this.has_a_tty.object(); - } - - setTTY(object) { - this.has_a_tty.setUp(object); + super(); + this.set(this.setUp(path)); } win32(path) { - return new TTY({ + return new MetaShell({ 'shell': process.env.COMSPEC, 'option': ['/c', 'start'], 'call': 'python', - 'path': path + 'script': path }); } darwin(path) { - return new TTY({ + return new MetaShell({ 'shell': 'osascript', 'option': ['-e'], 'call': 'python', - 'path': path + 'script': path }); } linux(path) { - return new TTY({ - 'shell': linux.terminal.shell, - 'option': [linux.terminal.option], + return new MetaShell({ + 'shell': linux.get().shell, + 'option': [linux.get().option], 'call': 'python', - 'path': path + 'script': path }); } setUp(path) { switch (process.platform) { case 'win32': - return this.win32(path); + return this.win32(path).get(); case 'darwin': - return this.darwin(path); + return this.darwin(path).get(); case 'linux': - return this.linux(path); + return this.linux(path).get(); default: - return undefined; + return null; } } } - -exports.console = new Console(); +exports.Base = Base; +exports.MetaShell = MetaShell; +exports.Shell = Shell; diff --git a/lib/terminal.js b/lib/terminal.js index 65f41a0..de604fb 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -1,147 +1,420 @@ +'use strict'; + + +// required for SpawnWrapper class +const spawn = require('child_process').spawn; + + /* - terminal.js - cross-platform compatible base for executing 3rd party apps - Copyright (C) 2017 xovertheyearsx@gmail.com + ########## + Base -- this class is the base class for all other classes + ########## + this is a special meta class that helps automate the constuction of objects on the fly. + + these objects are used to construct class Type objects contained by the main terminal object. + + class Type has 3 objects named has_a_tty, has_a_log, and spawn_tty. + + these objects are utilized by the end user and are modifiable. +*/ +class Base { + constructor(base, ...source){ + this.set(base, ...source) + } - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + get() { + return this.object; + } - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + set(base, ...source) { + if (null == base) { + this.object = new Object(); + } else if (null == source) { + this.object = Object.assign({}, base); + } else { + this.object = Object.assign(base, ...source); + } + // (function log(base, ...source) { + // console.log( + // `base: ${JSON.stringify(base)}\n` + + // `source: ${JSON.stringify(source)}\n` + + // `this: ${JSON.stringify(this.object)}\n` + // ); + // })(base, ...source); + } - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + isEmpty() { + for(var property in this.object) { + if(this.object.hasOwnProperty(property)) { + return false; + } + } + return JSON.stringify(this.object) === JSON.stringify({}); + } +} - - NOTE: Windows - windows xp and up can be gaurenteed to have the cmd.exe shell. - %comspec% should default to %windir%\system32\cmd.exe unless otherwise modified by the - end user. - https://en.wikipedia.org/wiki/Environment_variable#Windows +/* + ########## + LinuxSessionWrapper -- determine the default shell for the given desktop environment + ########## + this object determines the default shell, and the default shell option, by determining the users + current desktop environment (or session). - win32 has some weird path based bug from when spawn() spawns a tty to the point where it - calls and attempts to pass the path to cp/main.py. the command and path to the executable - must remain as seperate arguments to spawn. theoretically, command does not have to be - "start". it can just be cp/main.py and should spawn a new shell regardless. + this is for linux only because linux has a variety of desktop environments available. + if another shell is used, this can be modified via the Type instance through the has_a_tty - NOTE: Linux - linux has a mass variety of environments and a variety of ways - for determining those environments. best to go for the base - environments by depending on the most common builtin shells instead. - https://askubuntu.com/questions/72549/how-to-determine-which-window-manager-is-running + property value. +*/ +class LinuxSessionWrapper extends Base { + constructor() { + super(); + let shell = this.setupDefaultShell(); + let option = this.setupDefaultOption(shell); + this.set({ + 'shell': shell, + 'option': option + }); + }; - this just works regardless of how the arguments are passed. - as long as everything is recieved in the right way by cp/main.py, it should just work. + getSessionTerminal() { + switch (process.env.GDMSESSION) { + case 'ubuntu': + case 'ubuntu-2d': + case 'gnome': + case 'gnome-shell': + case 'gnome-classic': + case 'gnome-fallback': + case 'cinnamon': + return "gnome-terminal"; + case 'xfce': + return "xfce4-terminal"; + case 'kde-plasma': + return "konsole"; + case 'Lubuntu': + return "lxterminal"; + default: + return null; + } + } + + getDesktopTerminal() { + switch (process.env.XDG_CURRENT_DESKTOP) { + case 'Unity': + case 'GNOME': + case 'X-Cinnamon': + return "gnome-terminal"; + case 'XFCE': + return "xfce4-terminal"; + case 'KDE': + return "konsole"; + case 'LXDE': + return "lxterminal"; + default: + return "xterm"; + } + } - NOTE: Darwin - NOTE: osascript should always be used! consider your self warned! - NOTE: Darwin has a weird bug that causes multiple windows to spawn. + setupDefaultShell() { + let shell = this.getSessionTerminal(); - the default shell is determined by the Terminal.app settings. - the path method must be called in order to generate a valid command every time it is called. - the command property contains an empty primitive string otherwise. + if (null === shell) { + shell = this.getDesktopTerminal(); + } - darwin should never be modified or configured in any way. doing so risks breaking the - required tool chain. + return shell; + } + setupDefaultOption(shell) { + // determine the shells default execution option + switch (shell) { + case 'gnome-terminal': + case 'xfce4-terminal': + return "-x"; + case 'konsole': + case 'lxterminal': + case 'xterm': + return "-e"; + default: + return null; + } + } +}; - terminal.type.has_a_tty.shell - compatible shells listed by system type - "linux": - ["gnome-terminal", "konsole", "terminator", "xfce4-terminal", - "lxterminal", "uxterm", "xterm"] - uses default based on current desktop environment +/* + ########## + MetaShell -- a class used to create meta data for a terminal instance + ########## + this is a special meta class that helps automate the constuction of meta terminal objects on + the fly. - "win32": - ["cmd","powershell","bash"] - defaults to cmd + these objects are used to construct class Shell objects contained by the main terminal object. - "darwin": - ["osascript", "terminal"] - defaults to osascript + class Shell defines and creates the has_a_tty object for class Type. */ -'use strict'; +class MetaShell extends Base { + constructor(base, ...source) { + super(); + this.setBase(base, ...source); + this.setPath(base); + this.setCommand(); + } -const spawn = require('child_process').spawn; -const has_a_tty = require('./src/tty').has_a_tty; -const has_a_log = require('./src/log').has_a_log; + setBase(base, ...source) { + this.set({ + 'shell': base.shell, + 'option': base.option, + 'call': base.call + }, ...source); + } + + setPath(base) { + function defaultPath() { + if ('win32' === process.platform) { + return `${process.env.USERPROFILE}\\.atom\\packages\\atom-python-run\\cp\\main.py`; + } + return `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; + } + this.set(this.get(), { + 'script': (function(path) { + if (null == path) { + path = defaultPath(); + } + return path; + })(base.script) + }); + } + + setCommand() { + let object = this.get(); + this.set(object, { + 'command': function (...args) { + let path = object.call + " " + object.script; + for (let token of args) { + path += ` ${token}`; + } + return `tell app "Terminal" to do script "${path}"` + } + }); + } +} + + +/* + ########## + Shell -- a class used to create meta data for a terminal instance + ########## + this is class helps to automate the constuction of the terminal objects meta data. + + this meta data object is used to pass arguments to the child_process.spawn method. + + class Shell defines and creates the has_a_tty object for class Type. +*/ +class Shell extends Base { + constructor(path) { + super(); + this.set(this.setUp(path)); + } + + win32(path) { + return new MetaShell({ + 'shell': process.env.COMSPEC, + 'option': ['/c', 'start'], + 'call': 'python', + 'script': path + }); + } + darwin(path) { + return new MetaShell({ + 'shell': 'osascript', + 'option': ['-e'], + 'call': 'python', + 'script': path + }); + } + + linux(path) { + let session = new LinuxSessionWrapper(); + return new MetaShell({ + 'shell': session.get().shell, + 'option': [session.get().option], + 'call': 'python', + 'script': path + }); + } -let is_empty = function (object) { - for(var property in object) { - if(object.hasOwnProperty(property)) { - return false; + setUp(path) { + switch (process.platform) { + case 'win32': + return this.win32(path).get(); + case 'darwin': + return this.darwin(path).get(); + case 'linux': + return this.linux(path).get(); + default: + return null; } } - return JSON.stringify(object) === JSON.stringify({}); -}; +} + + +/* + CREATE A LOG OBJECT TO CAPTURE BASIC I/O OPERATIONS + + automate the logging process while executing in atom. + create a Log object that prints I/O operations to standard output. -let spawn_default_options = function (options) { - if (is_empty(options)) { - return { - 'cwd': undefined, - 'detached': false, - }; + this object prints all relavent information for inspecting basic I/O operations. + + this object should be used ONLY after spawn has executed and a tty instance has been created. +*/ +class Log extends Base { + constructor(base, ...source) { + super(); + this.set(base, ...source) } - return options; -}; + // default() { + // this.set({ + // 'tty': null, + // 'options': null, + // 'args': null, + // 'meta': null + // }); + // } -let spawn_win32 = function (object, options, ...args) { - return spawn( - object.has_a_tty.shell, - [ ...object.has_a_tty.option, - object.has_a_tty.call, - object.has_a_tty.script, - ...args - ], - options - ); -}; + metaData() { + console.log( + `platform: ${process.platform}\n` + + `shell: ${this.get().meta.shell}\n` + + `option: ${this.get().meta.option}\n` + + `call: ${this.get().meta.call}\n` + + `script: ${this.get().meta.script}\n` + + `command: ${this.get().meta.command(this.get().args)}\n` + ); + } + params() { + console.log( + `options: ${JSON.stringify(this.get().options, null, 4)}\n` + + `args: ${this.get().args}\n` + ); + } + + tty() { + this.get().tty.stdout.on('data', (data) => { + console.log(`stdout: ${data}`); + }); + this.get().tty.stderr.on('data', (data) => { + console.log(`stderr: ${data}`); + }); + this.get().tty.on('close', (code) => { + console.log(`child process exited with code ${code}`); + }); + } + + self() { + this.metaData(); + this.params(); + this.tty(); + } +} -let spawn_darwin = function (object, options, ...args) { - return spawn( - object.has_a_tty.shell, - [ object.has_a_tty.option, - object.has_a_tty.command(...args) - ], - options - ); -}; +/* -let spawns_tty = function (log, options, ...args) { - let tty; - let opts = spawn_default_options(options); - switch (process.platform) { - case 'linux': - case 'win32': - tty = spawn_win32(this, opts, ...args); - break; - case 'darwin': - tty = spawn_darwin(this, opts, ...args); - break; +*/ +class SpawnWrapper { + constructor(object) { + this.object = Object.assign({}, object); } - if (log) { - this.has_a_log(tty, opts, ...args); + + isEmpty(object) { + for(var property in object) { + if(object.hasOwnProperty(property)) { + return false; + } + } + return JSON.stringify(object) === JSON.stringify({}); } - return tty; -}; + defaultOptions(options) { + if (this.isEmpty(options)) { + return { + 'cwd': undefined, + 'detached': false, + }; + } + return options; + } -let Type = function () { - this.has_a_tty = has_a_tty; - this.has_a_log = has_a_log; - this.spawns_tty = spawns_tty; -}; + spawnWin32(object, options, ...args) { + return spawn( + object.has_a_tty.shell, + [ ...object.has_a_tty.option, + object.has_a_tty.call, + object.has_a_tty.script, + ...args + ], + options + ); + } + + spawnDarwin(object, options, ...args) { + return spawn( + object.has_a_tty.shell, + [ object.has_a_tty.option, + object.has_a_tty.command(...args) + ], + options + ); + } + + spawnWrapper(object, options, ...args) { + switch (process.platform) { + case 'linux': + case 'win32': + return this.spawnWin32(object, options, ...args); + case 'darwin': + return this.spawnDarwin(object, options, ...args); + default: + return null; + } + } + + spawnTTY(log, options, ...args) { + let opts = this.defaultOptions(options); + let tty = spawnWrapper(object, opts, ...args); + if (log) { + this.has_a_log(tty, opts, ...args); + } + return tty; + } +} + + +class MetaType { + constructor (object) { + this.has_a_tty = object.has_a_tty; + this.has_a_log = object.has_a_log; + this.spawn_tty = object.spawn_tty; + } +} + +// let linux; +// let shell; +// let log; +// let wrapper; + +exports.Base = Base; +exports.LinuxSessionWrapper = LinuxSessionWrapper; +exports.MetaShell = MetaShell; +exports.Shell = Shell; +exports.Log = Log; +exports.SpawnWrapper = SpawnWrapper; -exports.type = new Type(); +// exports.type = new MetaType(); From 368c536cf63424140ffb3fcf0c83011c0f083969 Mon Sep 17 00:00:00 2001 From: Austin Berrio Date: Wed, 30 Aug 2017 18:05:33 -0400 Subject: [PATCH 43/77] making final changes. still needs tweaking and minor bug fixes. --- lib/src/linux.js | 130 --------------------------------- lib/src/log.js | 77 -------------------- lib/src/spawn_wrapper.js | 81 --------------------- lib/src/tty.js | 152 --------------------------------------- lib/terminal.js | 118 +++++++++++++++--------------- lib/test.js | 17 +++++ 6 files changed, 76 insertions(+), 499 deletions(-) delete mode 100644 lib/src/linux.js delete mode 100644 lib/src/log.js delete mode 100644 lib/src/spawn_wrapper.js delete mode 100644 lib/src/tty.js create mode 100644 lib/test.js diff --git a/lib/src/linux.js b/lib/src/linux.js deleted file mode 100644 index a9b1901..0000000 --- a/lib/src/linux.js +++ /dev/null @@ -1,130 +0,0 @@ -/* - CREATE A LINUX CONSOLE WINDOW - - automatically determine the linux os desktop environment. - - create an object based on the current desktop session being used in linux. - - this object contains information for the default shell and the proper execution option to be passed to that shell. - - the methods get_shell() and get_option() can be used instead of the properties shell and option. - - this.shell and this.option are convenience properties. -*/ -'use strict'; - - -class Base { - constructor(base, ...source){ - this.set(base, ...source) - } - - get() { - return this.object; - } - - set(base, ...source) { - if (null == base) { - this.object = new Object(); - } else if (null == source) { - this.object = Object.assign({}, base); - } else { - this.object = Object.assign(base, ...source) - } - (function log(object, base, ...source) { - console.log( - `base: ${JSON.stringify(base)}\n` + - `source: ${JSON.stringify(source)}\n` + - `this: ${JSON.stringify(object.object)}\n` - ); - })(this, base, ...source); - } - - isEmpty() { - for(var property in this.object) { - if(this.object.hasOwnProperty(property)) { - return false; - } - } - return JSON.stringify(this.object) === JSON.stringify({}); - } -} - - -class LinuxSessionWrapper extends Base { - constructor() { - super(); - let shell = this.setupDefaultShell(); - let option = this.setupDefaultOption(shell); - this.set({ - 'shell': shell, - 'option': option - }); - }; - - getSessionTerminal() { - switch (process.env.GDMSESSION) { - case 'ubuntu': - case 'ubuntu-2d': - case 'gnome': - case 'gnome-shell': - case 'gnome-classic': - case 'gnome-fallback': - case 'cinnamon': - return "gnome-terminal"; - case 'xfce': - return "xfce4-terminal"; - case 'kde-plasma': - return "konsole"; - case 'Lubuntu': - return "lxterminal"; - default: - return null; - } - } - - getDesktopTerminal() { - switch (process.env.XDG_CURRENT_DESKTOP) { - case 'Unity': - case 'GNOME': - case 'X-Cinnamon': - return "gnome-terminal"; - case 'XFCE': - return "xfce4-terminal"; - case 'KDE': - return "konsole"; - case 'LXDE': - return "lxterminal"; - default: - return "xterm"; - } - } - - setupDefaultShell() { - let shell = this.getSessionTerminal(); - - if (null === shell) { - shell = this.getDesktopTerminal(); - } - - return shell; - } - - setupDefaultOption(shell) { - // determine the shells default execution option - switch (shell) { - case 'gnome-terminal': - case 'xfce4-terminal': - return "-x"; - case 'konsole': - case 'lxterminal': - case 'xterm': - return "-e"; - default: - return null; - } - } -}; - - -exports.session_wrapper = new LinuxSessionWrapper(); diff --git a/lib/src/log.js b/lib/src/log.js deleted file mode 100644 index 1889c12..0000000 --- a/lib/src/log.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - CREATE A LOG OBJECT TO CAPTURE BASIC I/O OPERATIONS - - automate the logging process while executing in atom. - - create a Log object that prints I/O operations to standard output. - - this object prints all relavent information for inspecting basic I/O operations. - - this object should be used ONLY after spawn has executed and a tty instance has been created. -*/ - -class Log { - constructor(object) { - if (null == object) { - this.defaultSetUp(); - } else { - this.setUp(object); - } - } - - defaultSetUp() { - this.setUp({ - 'tty': null, - 'options': null, - 'args': null, - 'metadata': null - }); - } - - setUp(object) { - this.tty = object.tty; - this.option = object.options; - this.arg = object.args; - this.object = object.metadata; - } - - logMetaData() { - console.log( - `platform: ${process.platform}\n` + - `has_a_tty:\n` + - `shell: ${this.object.has_a_tty.shell}\n` + - `option: ${this.object.has_a_tty.option}\n` + - `call: ${this.object.has_a_tty.call}\n` + - `script: ${this.object.has_a_tty.script}\n` + - `command: ${this.object.has_a_tty.command(...this.arg)}\n` - ); - } - - logParams() { - console.log( - "spawn_tty:\n" + - `\toptions:\n${JSON.stringify(this.option, null, 4)}\n` + - `\targs: ${[...this.arg]}\n` - ); - } - - logTTY(tty) { - this.tty.stdout.on('data', (data) => { - console.log(`stdout: ${data}`); - }); - this.tty.stderr.on('data', (data) => { - console.log(`stderr: ${data}`); - }); - this.tty.on('close', (code) => { - console.log(`child process exited with code ${code}`); - }); - } - - info() { - this.logMetaData(this.object); - this.logParams(this.option, ...this.arg); - this.logTTY(this.tty); - } -} - -exports.log = new Log(); diff --git a/lib/src/spawn_wrapper.js b/lib/src/spawn_wrapper.js deleted file mode 100644 index a9ee990..0000000 --- a/lib/src/spawn_wrapper.js +++ /dev/null @@ -1,81 +0,0 @@ -const spawn = require('child_process').spawn; -const console = require('./tty').console; -const log = require('./log').log; - - -class SpawnWrapper { - constructor(object) { - - } - - isEmpty(object) { - for(var property in object) { - if(object.hasOwnProperty(property)) { - return false; - } - } - return JSON.stringify(object) === JSON.stringify({}); - } - - defaultOptions(options) { - if (this.isEmpty(options)) { - return { - 'cwd': undefined, - 'detached': false, - }; - } - return options; - } - - spawnWin32(object, options, ...args) { - return spawn( - object.has_a_tty.shell, - [ ...object.has_a_tty.option, - object.has_a_tty.call, - object.has_a_tty.script, - ...args - ], - options - ); - } - - spawnDarwin(object, options, ...args) { - return spawn( - object.has_a_tty.shell, - [ object.has_a_tty.option, - object.has_a_tty.command(...args) - ], - options - ); - } - - spawnWrapper(object, options, ...args) { - switch (process.platform) { - case 'linux': - case 'win32': - return this.spawnWin32(object, options, ...args); - case 'darwin': - return this.spawnDarwin(object, options, ...args); - default: - return null; - } - } - - spawnTTY(log, options, ...args) { - let opts = this.defaultOptions(options); - let tty = spawnWrapper(object, opts, ...args); - if (log) { - this.has_a_log(tty, opts, ...args); - } - return tty; - } -} - - -class Type { - constructor (object) { - this.has_a_tty = object.has_a_tty; - this.has_a_log = object.has_a_log; - this.spawn_tty = object.spawn_tty; - } -} diff --git a/lib/src/tty.js b/lib/src/tty.js deleted file mode 100644 index 05ffd0f..0000000 --- a/lib/src/tty.js +++ /dev/null @@ -1,152 +0,0 @@ -/* - CREATE A META TERMINAL OBJECT THAT IS DEFINED BY OPERATING SYSTEM TYPE - - automatically create a TTY object. - - create a TTY object based on the current operating system. - - this object contains all the required information for executing a script within a tty instance. - - has_a_tty is the function to utilize in automating the process. -*/ -'use strict'; - - -const linux = require('./linux').session_wrapper; - - -class Base { - constructor(base, ...source){ - this.set(base, ...source) - } - - get() { - return this.object; - } - - set(base, ...source) { - if (null == base) { - this.object = new Object(); - } else if (null == source) { - this.object = Object.assign({}, base); - } else { - this.object = Object.assign(base, ...source) - } - (function log(object, base, ...source) { - console.log( - `base: ${JSON.stringify(base)}\n` + - `source: ${JSON.stringify(source)}\n` + - `this: ${JSON.stringify(object.object)}\n` - ); - })(this, base, ...source); - } - - isEmpty() { - for(var property in this.object) { - if(this.object.hasOwnProperty(property)) { - return false; - } - } - return JSON.stringify(this.object) === JSON.stringify({}); - } -} - - -class MetaShell extends Base { - constructor(base, ...source) { - super(); - this.setBase(base, ...source); - this.setPath(base); - this.setCommand(); - } - - setBase(base, ...source) { - this.set({ - 'shell': base.shell, - 'option': base.option, - 'call': base.call - }, ...source); - } - - setPath(base) { - function defaultPath() { - if ('win32' === process.platform) { - return `${process.env.USERPROFILE}\\.atom\\packages\\atom-python-run\\cp\\main.py`; - } - return `${process.env.HOME}/.atom/packages/atom-python-run/cp/main.py`; - } - this.set(this.get(), { - 'script': (function(path) { - if (null == path) { - return defaultPath(); - } - return path; - })(base.script) - }); - } - - setCommand() { - let object = this.get(); - this.set(object, { - 'command': function (...args) { - let path = object.call + " " + object.script; - for (let token of args) { - path += ` ${token}`; - } - return `tell app "Terminal" to do script "${path}"` - } - }); - } -} - - -class Shell extends Base { - constructor(path) { - super(); - this.set(this.setUp(path)); - } - - win32(path) { - return new MetaShell({ - 'shell': process.env.COMSPEC, - 'option': ['/c', 'start'], - 'call': 'python', - 'script': path - }); - } - - darwin(path) { - return new MetaShell({ - 'shell': 'osascript', - 'option': ['-e'], - 'call': 'python', - 'script': path - }); - } - - linux(path) { - return new MetaShell({ - 'shell': linux.get().shell, - 'option': [linux.get().option], - 'call': 'python', - 'script': path - }); - } - - setUp(path) { - switch (process.platform) { - case 'win32': - return this.win32(path).get(); - case 'darwin': - return this.darwin(path).get(); - case 'linux': - return this.linux(path).get(); - default: - return null; - } - } -} - -exports.Base = Base; -exports.MetaShell = MetaShell; -exports.Shell = Shell; diff --git a/lib/terminal.js b/lib/terminal.js index de604fb..23a4f5c 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -289,34 +289,34 @@ class Log extends Base { metaData() { console.log( `platform: ${process.platform}\n` + - `shell: ${this.get().meta.shell}\n` + - `option: ${this.get().meta.option}\n` + - `call: ${this.get().meta.call}\n` + - `script: ${this.get().meta.script}\n` + - `command: ${this.get().meta.command(this.get().args)}\n` + `shell: ${this.object.meta.shell}\n` + + `option: ${this.object.meta.option}\n` + + `call: ${this.object.meta.call}\n` + + `script: ${this.object.meta.script}\n` + + `command: ${this.object.meta.command(this.get().args)}\n` ); } params() { console.log( - `options: ${JSON.stringify(this.get().options, null, 4)}\n` + - `args: ${this.get().args}\n` + `options: ${JSON.stringify(this.object.options, null, 4)}\n` + + `args: ${this.object.args}\n` ); } tty() { - this.get().tty.stdout.on('data', (data) => { + this.object.tty.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); - this.get().tty.stderr.on('data', (data) => { + this.object.tty.stderr.on('data', (data) => { console.log(`stderr: ${data}`); }); - this.get().tty.on('close', (code) => { + this.object.tty.on('close', (code) => { console.log(`child process exited with code ${code}`); }); } - self() { + call() { this.metaData(); this.params(); this.tty(); @@ -327,22 +327,22 @@ class Log extends Base { /* */ -class SpawnWrapper { - constructor(object) { - this.object = Object.assign({}, object); +class SpawnWrapper extends Base { + constructor(base, ...source) { + super(); + this.set(base, ...source); } - isEmpty(object) { - for(var property in object) { - if(object.hasOwnProperty(property)) { - return false; + optionWrapper(options) { + function isEmpty(object) { + for(var property in object) { + if(object.hasOwnProperty(property)) { + return false; + } } + return JSON.stringify(object) === JSON.stringify({}); } - return JSON.stringify(object) === JSON.stringify({}); - } - - defaultOptions(options) { - if (this.isEmpty(options)) { + if (isEmpty(options)) { return { 'cwd': undefined, 'detached': false, @@ -351,70 +351,70 @@ class SpawnWrapper { return options; } - spawnWin32(object, options, ...args) { + spawnWin32(options, ...args) { + let opts = this.object.option; return spawn( - object.has_a_tty.shell, - [ ...object.has_a_tty.option, - object.has_a_tty.call, - object.has_a_tty.script, + this.object.shell, + [ ...this.object.option, + this.object.call, + this.object.script, ...args ], options ); } - spawnDarwin(object, options, ...args) { + spawnDarwin(options, ...args) { return spawn( - object.has_a_tty.shell, - [ object.has_a_tty.option, - object.has_a_tty.command(...args) + this.object.shell, + [ this.object.option, + this.object.command(...args) ], options ); } - spawnWrapper(object, options, ...args) { + spawnWrapper(options, ...args) { switch (process.platform) { case 'linux': case 'win32': - return this.spawnWin32(object, options, ...args); + return this.spawnWin32(options, ...args); case 'darwin': - return this.spawnDarwin(object, options, ...args); + return this.spawnDarwin(options, ...args); default: return null; } } - spawnTTY(log, options, ...args) { - let opts = this.defaultOptions(options); - let tty = spawnWrapper(object, opts, ...args); - if (log) { - this.has_a_log(tty, opts, ...args); + tty(object) { + let args = object.args; + let options = this.optionWrapper(object.options); + let tty = this.spawnWrapper(options, ...args); + if (object.log) { + let log = new Log({ + 'tty': tty, + 'options': options, + 'args': args, + 'meta': this.object + }); + log.call(); } return tty; } } -class MetaType { - constructor (object) { - this.has_a_tty = object.has_a_tty; - this.has_a_log = object.has_a_log; - this.spawn_tty = object.spawn_tty; +class Type { + constructor () { + this.has_a_tty = new Shell().object; + this.spawn = new SpawnWrapper(this.has_a_tty); } } -// let linux; -// let shell; -// let log; -// let wrapper; - -exports.Base = Base; -exports.LinuxSessionWrapper = LinuxSessionWrapper; -exports.MetaShell = MetaShell; -exports.Shell = Shell; -exports.Log = Log; -exports.SpawnWrapper = SpawnWrapper; - - -// exports.type = new MetaType(); +// exports.Base = Base; +// exports.LinuxSessionWrapper = LinuxSessionWrapper; +// exports.MetaShell = MetaShell; +// exports.Shell = Shell; +// exports.Log = Log; +// exports.SpawnWrapper = SpawnWrapper; +exports.Type = new Type(); diff --git a/lib/test.js b/lib/test.js new file mode 100644 index 0000000..c36b3aa --- /dev/null +++ b/lib/test.js @@ -0,0 +1,17 @@ +'use strict'; + + +const terminal = require('./terminal'); + +let object = { + 'log': true, + 'options': {}, + 'args': [ + 'python', + `${process.env.HOME}/Documents/Python/hello.py` + ] +} + +let type = new terminal.Type(); + +type.spawn.tty(object); From aa052afb0d7b6ba16e5668f4508e5528685fad57 Mon Sep 17 00:00:00 2001 From: jargon Date: Fri, 1 Sep 2017 01:19:47 -0400 Subject: [PATCH 44/77] refactored, cleaned up, and worked out some bugs. still needs testing. --- lib/atom-python-run.js | 121 +++++++++++++-------------------- lib/terminal.js | 147 +++++++++++++++++++---------------------- lib/test.js | 7 +- 3 files changed, 116 insertions(+), 159 deletions(-) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index b16ae5b..30d21e0 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -1,31 +1,8 @@ -/* - NOTE: win32 and linux only! - NOTE: do not modify the has_a_tty properties on mac os x! - - terminal.type.spawns_tty executes these properties when called. - each property can me modified according to your personal needs. - - modify the default shell - terminal.type.has_a_tty.shell = "shell_name_here"; - - modify the options passed to shell - terminal.type.has_a_tty.option = "--options-go-here"; - - modify the the command - default (win32 only) - if ("win32" === process.platform) terminal.type.has_a_tty.command = "start"; - default (darwin only) - if ("darwin" === process.platform) { - const stringify = terminal.type.has_a_tty.command; - terminal.type.has_a_tty.command = stringify(...args); - } -*/ -/* global atom */ 'use strict'; const CompositeDisposable = require('atom').CompositeDisposable; const path = require('path'); -let terminal = require('./terminal'); +const terminal = require('./terminal'); module.exports = { @@ -52,22 +29,6 @@ module.exports = { type: 'string', default: 'python {file}', }, - // NOTE: I suppose most of the users don't want to see these - // notifications, this is experimental removal in v1.0.0 - // -- Infinity - // - // disable_notifications: { - // title: 'Disable success notifications', - // description: 'Disable notifications while saving and running', - // type: 'boolean', - // default: false - // }, - // disable_notifications_on_fail: { - // title: 'Disable failure notifications', - // description: 'Disable notifications when extension name does not match', - // type: 'boolean', - // default: false - // }, extension_filter: { title: 'Extension filter', description: 'Leave it empty it accepts all files, or you can try `.py`', @@ -103,49 +64,57 @@ function run_f6() { atom.config.get('atom-python-run.f6_no_cp')); } -function run(command, nocp) { +function run(command, cp) { let editor = atom.workspace.getActiveTextEditor(); - if (!editor) { - return; - } let file = editor.buffer.file; - if (!file) { - atom.notifications.add('warning', 'You have to create the file first.'); - return; + let info = path.parse(file.path); + let extensions = atom.config.get('atom-python-run.extension_filter'); + let args = command.split(' '); + let shell = new terminal.Shell(); + let spawn = new terminal.SpawnWrapper(shell.object); + + function invalid(extensions) { + if (extensions.length && !extensions.includes(info.ext)) { + return true; + } + return false; } - // information('Saving...'); + + function parse(array) { + function format(string, object) { + return string.replace(/{.*?}/g, element => object[element.substring(1, element.length - 1)]); + } + array.forEach((item, index, arr) => { + arr[index] = format(item, { + 'file': file.path, + 'dir': info.dir, + 'name': info.name, + 'ext': info.ext, + }); + }); + } + editor.save(); - let info = path.parse(file.path); - let filter = atom.config.get('atom-python-run.extension_filter'); - if (filter.length && !filter.includes(info.ext)) { + + if (invalid(extensions)) { return; } - // information(`Running ${info.base} ...`); - let cmds = command.split(' '); // TODO: double quote? - cmds.forEach((k, i, a) => { - a[i] = k.format({ - 'file': file.path, - 'dir': info.dir, - 'name': info.name, - 'ext': info.ext, - }); + + parse(args); + + let tty = spawn.tty({ + 'log': true, + 'options': { + cwd: info.dir, + detached: true + }, + 'cp': cp, + 'args': [...args] }); - let log = true; - let options = { - cwd: info.dir, - detached: true, - }; - cmds = [log, options, nocp, ...cmds]; - let tty = terminal.type.spawns_tty(...cmds); + tty.unref(); } -// function information(message) { -// if (!atom.config.get('atom-python-run.disable_notifications')) { -// atom.notifications.add('info', String(message)); -// } -// } - -String.prototype.format = function(array) { - return this.replace(/{.*?}/g, k => array[k.substring(1, k.length - 1)]); -}; +// String.prototype.format = function(object) { +// return this.replace(/{.*?}/g, element => object[element.substring(1, element.length - 1)]); +// }; diff --git a/lib/terminal.js b/lib/terminal.js index 23a4f5c..2f5b9b1 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -34,13 +34,6 @@ class Base { } else { this.object = Object.assign(base, ...source); } - // (function log(base, ...source) { - // console.log( - // `base: ${JSON.stringify(base)}\n` + - // `source: ${JSON.stringify(source)}\n` + - // `this: ${JSON.stringify(this.object)}\n` - // ); - // })(base, ...source); } isEmpty() { @@ -209,7 +202,7 @@ class MetaShell extends Base { this meta data object is used to pass arguments to the child_process.spawn method. - class Shell defines and creates the has_a_tty object for class Type. + class Shell defines and creates the object to be passed to SpawnWrapper */ class Shell extends Base { constructor(path) { @@ -261,14 +254,12 @@ class Shell extends Base { /* - CREATE A LOG OBJECT TO CAPTURE BASIC I/O OPERATIONS - - automate the logging process while executing in atom. - - create a Log object that prints I/O operations to standard output. - - this object prints all relavent information for inspecting basic I/O operations. + ########## + Log -- automate the logging process while executing in atom. + ########## + create a log object to capture and print basic i/o operations to standard output. + NOTE: this object should be used ONLY after spawn has executed and a tty instance has been created. */ class Log extends Base { @@ -277,15 +268,6 @@ class Log extends Base { this.set(base, ...source) } - // default() { - // this.set({ - // 'tty': null, - // 'options': null, - // 'args': null, - // 'meta': null - // }); - // } - metaData() { console.log( `platform: ${process.platform}\n` + @@ -293,14 +275,14 @@ class Log extends Base { `option: ${this.object.meta.option}\n` + `call: ${this.object.meta.call}\n` + `script: ${this.object.meta.script}\n` + - `command: ${this.object.meta.command(this.get().args)}\n` + `command: ${this.object.meta.command(this.get().args)}` ); } params() { console.log( `options: ${JSON.stringify(this.object.options, null, 4)}\n` + - `args: ${this.object.args}\n` + `args: ${this.object.args}` ); } @@ -325,7 +307,22 @@ class Log extends Base { /* + ########## + SpawnWrapper -- creates a spawn wrapper object + ########## + This object determines how to pass arguments to the invoked shell instance. + + The object passed to this wrapper is the Shell object. The instance of the Shell + object is used to determine the call that is made to the child_process.spawn method. + + Once a wrapper instance has been instantiated, the only method that should be called + is the tty() method. + + SpawnWrapper.tty() will return the active object after execution. + NOTE: + Even though this object requires a Shell instance object to instantiate itself, + this process is mainly automated. */ class SpawnWrapper extends Base { constructor(base, ...source) { @@ -333,88 +330,78 @@ class SpawnWrapper extends Base { this.set(base, ...source); } - optionWrapper(options) { - function isEmpty(object) { - for(var property in object) { - if(object.hasOwnProperty(property)) { - return false; - } + spawnWin32(model) { + function runCp(...args) { + if (model.cp) { + return [...this.object.option, this.object.call, this.object.script, ...args]; + } else { + return [...this.object.option, ...args] } - return JSON.stringify(object) === JSON.stringify({}); - } - if (isEmpty(options)) { - return { - 'cwd': undefined, - 'detached': false, - }; } - return options; - } - - spawnWin32(options, ...args) { - let opts = this.object.option; return spawn( this.object.shell, - [ ...this.object.option, - this.object.call, - this.object.script, - ...args - ], - options - ); + [...runCp(model.args)], + model.options + ) } - spawnDarwin(options, ...args) { + spawnDarwin(model) { + // darwin should never be modified or configured in any way. doing + // so risks breaking the required tool chain. return spawn( this.object.shell, [ this.object.option, - this.object.command(...args) + this.object.command(...model.args) ], - options + model.options ); } - spawnWrapper(options, ...args) { + spawnWrapper(model) { switch (process.platform) { case 'linux': case 'win32': - return this.spawnWin32(options, ...args); + return this.spawnWin32(model); case 'darwin': - return this.spawnDarwin(options, ...args); + return this.spawnDarwin(model); default: return null; } } - tty(object) { - let args = object.args; - let options = this.optionWrapper(object.options); - let tty = this.spawnWrapper(options, ...args); + optionWrapper(options) { + function isEmpty(object) { + for(var property in object) { + if(object.hasOwnProperty(property)) { + return false; + } + } + return JSON.stringify(object) === JSON.stringify({}); + } + if (isEmpty(options)) { + options = { + 'cwd': undefined, + 'detached': false + }; + } + return options; + } + + tty(model) { + object.options = this.optionWrapper(object.options); + let tty = this.spawnWrapper(model); if (object.log) { - let log = new Log({ + new Log({ 'tty': tty, - 'options': options, - 'args': args, + 'args': model.args, + 'options': model.options, 'meta': this.object - }); - log.call(); + }).call(); } return tty; } } -class Type { - constructor () { - this.has_a_tty = new Shell().object; - this.spawn = new SpawnWrapper(this.has_a_tty); - } -} - -// exports.Base = Base; -// exports.LinuxSessionWrapper = LinuxSessionWrapper; -// exports.MetaShell = MetaShell; -// exports.Shell = Shell; -// exports.Log = Log; -// exports.SpawnWrapper = SpawnWrapper; -exports.Type = new Type(); +exports.Shell = Shell; +exports.SpawnWrapper = SpawnWrapper; diff --git a/lib/test.js b/lib/test.js index c36b3aa..753b239 100644 --- a/lib/test.js +++ b/lib/test.js @@ -1,6 +1,5 @@ 'use strict'; - const terminal = require('./terminal'); let object = { @@ -12,6 +11,8 @@ let object = { ] } -let type = new terminal.Type(); +let shell = new terminal.Shell(); + +let spawn = new terminal.SpawnWrapper(shell.object); -type.spawn.tty(object); +spawn.tty(object); From ee950d5dc4e8d93590a8f7d4ae341c977923121b Mon Sep 17 00:00:00 2001 From: jargon Date: Sat, 2 Sep 2017 00:32:28 -0400 Subject: [PATCH 45/77] refactored and cleaned up code base. ironed out a few bugs and errors. --- lib/atom-python-run.js | 30 +++++------ lib/terminal.js | 120 +++++++++++++++++++++++++++++++---------- lib/test.js | 1 + 3 files changed, 107 insertions(+), 44 deletions(-) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index 30d21e0..fb8dddc 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -38,17 +38,17 @@ module.exports = { }, default: [], }, - f5_no_cp: { - title: 'Exit immediately when program ends (F5)', - description: 'Don\'t show time elasped and don\'t pause.', + f5_cp: { + title: 'Use `cp` (F5)', + description: 'Show elapsed time and pause', type: 'boolean', - default: false, + default: true, }, - f6_no_cp: { - title: 'Exit immediately when program ends (F6)', - description: 'Don\'t show time elasped and don\'t pause.', + f6_cp: { + title: 'Use `cp` (F6)', + description: 'Show elapsed time and pause', type: 'boolean', - default: false, + default: true, }, }, subscriptions: null, @@ -56,12 +56,12 @@ module.exports = { function run_f5() { run(atom.config.get('atom-python-run.f5_command'), - atom.config.get('atom-python-run.f5_no_cp')); + atom.config.get('atom-python-run.f5_cp')); } function run_f6() { run(atom.config.get('atom-python-run.f6_command'), - atom.config.get('atom-python-run.f6_no_cp')); + atom.config.get('atom-python-run.f6_cp')); } function run(command, cp) { @@ -104,17 +104,13 @@ function run(command, cp) { let tty = spawn.tty({ 'log': true, + 'cp': cp, + 'args': args, 'options': { cwd: info.dir, detached: true - }, - 'cp': cp, - 'args': [...args] + } }); tty.unref(); } - -// String.prototype.format = function(object) { -// return this.replace(/{.*?}/g, element => object[element.substring(1, element.length - 1)]); -// }; diff --git a/lib/terminal.js b/lib/terminal.js index 2f5b9b1..a5a35e3 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -9,13 +9,19 @@ const spawn = require('child_process').spawn; ########## Base -- this class is the base class for all other classes ########## - this is a special meta class that helps automate the constuction of objects on the fly. + A base object is used in the construction of other objects. - these objects are used to construct class Type objects contained by the main terminal object. + The object is named 'object' and stored within the constructed object as a + property. - class Type has 3 objects named has_a_tty, has_a_log, and spawn_tty. + This property can be accessed by using the get() and set() methods. - these objects are utilized by the end user and are modifiable. + The object property can be accessed directly as well for convenience and is + named object for generic reference purposes because the name of an object + is indeterminate. + + The isEmpty() method can be used to figure out whether the object contains + a value or not. */ class Base { constructor(base, ...source){ @@ -51,13 +57,17 @@ class Base { ########## LinuxSessionWrapper -- determine the default shell for the given desktop environment ########## - this object determines the default shell, and the default shell option, by determining the users - current desktop environment (or session). + This object determines the default shell, and the default shell option, by + determining the users current desktop environment (or session). + + By default, once a LinuxSessionWrapper object has be created, it will + contain the default values which are appropriate for that Desktop Environment. - this is for linux only because linux has a variety of desktop environments available. - if another shell is used, this can be modified via the Type instance through the has_a_tty + This is only for linux because linux has a variety of desktop environments + available. - property value. + If another shell is used, this can be modified later during instantiation of + a Shell object by modifying the shell and option property values. */ class LinuxSessionWrapper extends Base { constructor() { @@ -139,12 +149,29 @@ class LinuxSessionWrapper extends Base { ########## MetaShell -- a class used to create meta data for a terminal instance ########## - this is a special meta class that helps automate the constuction of meta terminal objects on - the fly. + This is a blueprint for the Shell class. + + MetaShell is a special meta class that helps automate the constuction of + metadata used to create a terminal instance. - these objects are used to construct class Shell objects contained by the main terminal object. + These objects are used to construct Shell instance objects utilized by the + instantiated terminal object. - class Shell defines and creates the has_a_tty object for class Type. + While this object can be changed once instantiated by utilizing the + setBase() method, it's typically best to pass any arguments to the + constructor instead. + + All MetaShell objects have 5 properties: + - shell The terminal instance to be created + - option The terminal instance execution option + - call A REPL or Interpreter that executes 'script' + - script A program to execute given arguments within a 'shell' + - command The commands to be executed by 'script' (Mac OS X Only) + + i.e. : + $ shell option call script + translates to + $ gnome-terminal -x python script.py */ class MetaShell extends Base { constructor(base, ...source) { @@ -198,11 +225,17 @@ class MetaShell extends Base { ########## Shell -- a class used to create meta data for a terminal instance ########## - this is class helps to automate the constuction of the terminal objects meta data. + The Shell class creates a Shell object according to its local environment. + + A 'path' can be passed to the constructor, appropriate method according to + the local environment, or by manually invoking the setUp() method which + determines the local environment automatically. - this meta data object is used to pass arguments to the child_process.spawn method. + The Shell object is used to define the metadata that is passed to a + SpawnWrapper instance call which is then used to create a . - class Shell defines and creates the object to be passed to SpawnWrapper + 'path' should always be a string. If 'path' is undefined, a default path + is automatically set instead. */ class Shell extends Base { constructor(path) { @@ -257,10 +290,26 @@ class Shell extends Base { ########## Log -- automate the logging process while executing in atom. ########## - create a log object to capture and print basic i/o operations to standard output. + Create a log object to capture and print basic i/o operations to + standard output. + + A Log call will require an object during instantiation with the following + format: + - tty Requires a instance + - options The options object passed to spawn() + - args The parameters to be executed by the terminal instance + - cp A boolean value that determines a terminal instance call + - meta The metadata to be passed to a terminal instance call + + The 'meta' property should contain a Shell instance object because the + metaData() method expects specific properties to be available. + + This object should be used ONLY after spawn has executed and a tty instance + has been created. NOTE: - this object should be used ONLY after spawn has executed and a tty instance has been created. + At least one key/value pair is required to create a valid Log instance, but + will ultimately fail once the call() method is executed. */ class Log extends Base { constructor(base, ...source) { @@ -282,7 +331,8 @@ class Log extends Base { params() { console.log( `options: ${JSON.stringify(this.object.options, null, 4)}\n` + - `args: ${this.object.args}` + `args: ${this.object.args}\n` + + `cp: ${this.object.cp}` ); } @@ -310,7 +360,18 @@ class Log extends Base { ########## SpawnWrapper -- creates a spawn wrapper object ########## - This object determines how to pass arguments to the invoked shell instance. + SpawnWrapper class creates a child_process.spawn() wrapper object. + + A Shell.object is passed to SpawnWrapper during instantiation. + + The Shell.tty() method is used to create a object. + + The tty() method takes a single object called 'model' which is a psuedo + representation of how the object should be created. + - log A flag based Boolean value + - cp Ditto... + - args An array of arguments to be executed by a terminal instance + - options The 'options' object passed to child_process.spawn() The object passed to this wrapper is the Shell object. The instance of the Shell object is used to determine the call that is made to the child_process.spawn method. @@ -331,16 +392,16 @@ class SpawnWrapper extends Base { } spawnWin32(model) { - function runCp(...args) { + function cp(object, ...args) { if (model.cp) { - return [...this.object.option, this.object.call, this.object.script, ...args]; + return [...object.option, object.call, object.script, ...args]; } else { - return [...this.object.option, ...args] + return [...object.option, ...args]; } } return spawn( this.object.shell, - [...runCp(model.args)], + [...cp(this.object, ...model.args)], model.options ) } @@ -350,7 +411,7 @@ class SpawnWrapper extends Base { // so risks breaking the required tool chain. return spawn( this.object.shell, - [ this.object.option, + [ ...this.object.option, this.object.command(...model.args) ], model.options @@ -388,13 +449,14 @@ class SpawnWrapper extends Base { } tty(model) { - object.options = this.optionWrapper(object.options); + model.options = this.optionWrapper(model.options); let tty = this.spawnWrapper(model); - if (object.log) { + if (model.log) { new Log({ 'tty': tty, 'args': model.args, 'options': model.options, + 'cp': model.cp, 'meta': this.object }).call(); } @@ -403,5 +465,9 @@ class SpawnWrapper extends Base { } +// exports.Base = Base; +// exports.LinuxSessionWrapper = LinuxSessionWrapper; +// exports.MetaShell = MetaShell; +// exports.Log = Log; exports.Shell = Shell; exports.SpawnWrapper = SpawnWrapper; diff --git a/lib/test.js b/lib/test.js index 753b239..d5a6188 100644 --- a/lib/test.js +++ b/lib/test.js @@ -4,6 +4,7 @@ const terminal = require('./terminal'); let object = { 'log': true, + 'cp': true, 'options': {}, 'args': [ 'python', From 203aef0faadaa9f4ad1b83a059128851dd690a23 Mon Sep 17 00:00:00 2001 From: jargon Date: Tue, 5 Sep 2017 20:13:31 -0400 Subject: [PATCH 46/77] cleaned up code base and fixed bugs. --- cp/cp/cplib.py | 96 ++++++++++--------------- cp/cp/parse.py | 143 ++++++++++++++++++-------------------- cp/main.py | 3 +- lib/atom-python-run.js | 2 +- lib/terminal.js | 154 +++-------------------------------------- 5 files changed, 118 insertions(+), 280 deletions(-) diff --git a/cp/cp/cplib.py b/cp/cp/cplib.py index 0d5acfe..abe84c6 100644 --- a/cp/cp/cplib.py +++ b/cp/cp/cplib.py @@ -2,62 +2,24 @@ # NOTE: 2to3 and 3to2 Compatability # importing should consider 2to3 and 3to2 implications. # -# for both backwards and forwards compatability, print_function and clock -# are imported to prevent incompatability issues that may arrise. -# -# clock() is deprecated in python >= 3.3 -# process_time has not been ported back to 2.x -# source: https://stackoverflow.com/questions/85451/python-time-clock-vs-time-time-accuracy#85533 -# -# for all intents and purposes, clock functions very much the same as it does -# in both C and C++ -# source: https://docs.python.org/2.7/library/time.html#time.clock -# -# to avoid naming conflicts, process_time is imported as clock for 3.3 onwards -# -# for measuring snippets and algorithms, the built-in timeit module should be used instead -# source: https://docs.python.org/2/library/timeit.html -# -# clock()s value depends on the CPU, Arch type, OS, etc... -# this means the value returned by clock() is indeterminate -# -# the common averages for the given systems are -# on win32, a microsecond or 1/1000000 of a second -# on linux, a millisecond or 1/1000 of a second -# -# for compatability, str.format() is used instead of the print % modifier +# https://stackoverflow.com/questions/85451/python-time-clock-vs-time-time-accuracy#85533 +# https://docs.python.org/3/library/time.html#time.time +# https://docs.python.org/2.7/library/time.html#time.clock +# https://docs.python.org/2/library/timeit.html +# https://docs.python.org/2/library/sys.html#sys.platform # https://docs.python.org/2/library/string.html#formatstrings # -# NOTE: set_logpath() issues -# if logpath becomes an issue on win32 platforms (mainly with special characters in the path), -# this may be remedied by escaping that special character. -# -# special_char = '&' -# escaped_special_char = '\&' -# logpath.split(special_char) -# escaped_special_char.join(logpath) -# -# this may be arbitrary even though the most commonly used symbol may be the ampersand (&). -# i refrained from implementing this as it may cause more issues than the one it would solve. -# -# for example, you could inadvertantly create a seperated path looking like this -# from -> c:\\Users\\User&Name -# to -> c:\\Users\\User\\&Name -# -# using pythons raw strings may be a work around to this issue, but thats for another time -# -# NOTE: Determine platform Type -# https://docs.python.org/2/library/sys.html#sys.platform -# from __future__ import absolute_import +from __future__ import print_function from os import system, environ from os.path import isdir, dirname from subprocess import call -from time import time as clock +from time import time from sys import platform import logging from .parse import Parser + __all__ = ( 'set_log_path', 'set_namespace', 'set_command', 'set_clock', 'print_clock', @@ -65,9 +27,13 @@ ) -version_info = "v0.0.10" +version_info = "v0.1.1" +# +# NOTE: +# an improved methodology for determining the defaul log path is needed +# def set_log_path(): if 'win32' == platform: log = "{}\\.atom\\packages\\atom-python-run\\cp.log".format(environ['USERPROFILE']) @@ -97,24 +63,38 @@ def set_command(namespace): command.append(opt) command.append(namespace.script) if namespace.pipe: - command.append(namespace.pipe_symbol) command.append(namespace.pipe_file) logging.debug('command: %s', command) return command -def set_clock(command): - t = clock() - r = call(command) - t = clock() - t - logging.info('return code: %d, execution time: %.6f', r, t) - return r, t +def set_call(command, namespace, mode): + code = None + if namespace.pipe: + with open(namespace.pipe_file, mode) as f: + code = call(command, stdout=f, stderr=f) + f.close() + else: + code = call(command) + return code + + +def log_clock(code, clock): + logging.info('return code: %d (0x%x)', code, code) + logging.info('elapsed time: %.6f', clock) + + +def set_clock(command, namespace=None, mode='a'): + clock = time() + code = set_call(command, namespace, mode) + clock = time() - clock + log_clock(code, clock) + return code, clock -def print_clock(code, time): - print( - "\nProcess returned {:d} (0x{:x}) execution time : {:.3f} s".format(code, code, time) - ) +def print_clock(code, clock): + print("\nProcess returned {:d} (0x{:x})".format(code, code), end="\t") + print("execution time : {:.3f} s".format(clock)) def pause(): diff --git a/cp/cp/parse.py b/cp/cp/parse.py index d16d31d..ad45ed6 100644 --- a/cp/cp/parse.py +++ b/cp/cp/parse.py @@ -1,64 +1,68 @@ -""" -parser - handles arbitrary arguments to be executed. - -NOTE: use the main.py file instead if you want to execute scripts. while parse.py is executable, -it executes as a dummy file and doesn't actually execute anything directly. it simply demonstrates -how parse.py handles the argv variable. - -argv is a list and join could be used in conjunction to create a concatenated string. - -this string could be passed directly to command. it is up to the end user to make sure the -command executes as intended. - -parse.py is mainly designed to be an extremely lightweight check. - -it's rules restrict what can be passed to it while remaining flexible enough to be able -achieve arbitrary execution. - -in example: - -this will always fail... and should always fail. - -> python is the interpreter to be executed by parse.py - -> -c is the option to be passed to python (not parse.py) - -> string is the command to be executed by python (not parse.py) - -the point of the file check is to determine if the file actually exists before attempting to read -it; not to see if it is executable. we also have no intention of writing to it. obviously, a string -is executable, but we want to execute a file; not a string. - - $ python parse.py python -c 'print("hello, world!")' - IOError: file does not exist: print("hello, world!") - - $ python -c "print('Hello, World!')" - bash: !': event not found - - $ python -c 'print("hello, world!")' - hello, world - -parse.py will handle user and variable expansion. globs should never be expanded. -the final argument should always be a source file that can be executed by the given interpreter. - - $ python parse.py node ~/documents/js/hello.js - ...dictionary output here... - -parse.py will handle its own options as long as they are placed before the interpreter argument. -any arguments inbetween the interpreter and the filename are considered options to be passed to the -interpreter itself. - -parse.py also handles redirection. for example, if you want to pipe stdout while appending to a -file, you can do this - - $ python parse.py -p out.log '>>' node ~/documents/js/hello.js - ...dictionary output here... - -for more usage information, do - - $ python parse.py -h - ...help output here... - -an example on how to use the Parser object is demonstrated at the end of the file -""" - +"""parser - handles arbitrary arguments to be executed.""" +# NOTE: use the main.py file instead if you want to execute scripts. +# while parse.py is executable, it executes as a dummy file and doesn't actually +# execute anything directly. it simply demonstrates how parse.py handles the +# argv variable. +# +# argv is a list and join could be used in conjunction to create a concatenated +# string. +# +# this string could be passed directly to command. it is up to the end user to +# make sure the command executes as intended. +# +# parse.py is mainly designed to be an extremely lightweight check. +# +# it's rules restrict what can be passed to it while remaining flexible enough +# to be able achieve arbitrary execution. +# +# in example: +# +# this will always fail... and should always fail. +# -> python is the interpreter to be executed by parse.py +# -> -c is the option to be passed to python (not parse.py) +# -> string is the command to be executed by python (not parse.py) +# +# the point of the file check is to determine if the file actually exists before +# attempting to read it; not to see if it is executable. we also have no +# intention of writing to it. obviously, a string is executable, but we want +# to execute a file; not a string. +# +# $ python parse.py python -c 'print("hello, world!")' +# IOError: file does not exist: print("hello, world!") +# +# $ python -c "print('Hello, World!')" +# bash: !': event not found +# +# $ python -c 'print("hello, world!")' +# hello, worlds +# +# parse.py will handle user and variable expansion. +# globs should never be expanded. +# the final argument should always be a source file that can be executed by the +# given interpreter. +# +# $ python parse.py node ~/documents/js/hello.js +# ...dictionary output here... +# +# parse.py will handle its own options as long as they are placed before the +# interpreter argument. +# any arguments inbetween the interpreter and the filename are considered +# options to be passed to the interpreter itself. +# +# parse.py also handles redirection. for example, if you want to pipe stdout +# while appending to a file, you can do this +# +# $ python parse.py -p out.log '>>' node ~/documents/js/hello.js +# ...dictionary output here... +# +# for more usage information, do +# +# $ python parse.py -h +# ...help output here... +# +# an example on how to use the Parser object is demonstrated at the end of +# the file +# from __future__ import print_function from sys import argv from os.path import isfile, expanduser, expandvars @@ -152,7 +156,7 @@ def _set_script(self): class SimpleOptParser(Namespace): def isvalid(self, argument): - if argument in self._options or argument in self._symbols: + if argument in self._options: return True return False @@ -168,20 +172,10 @@ def _set_pipe_file(self): self.shift() self.namespace.pipe_file = self.get_arg('no file to pipe to') - def _set_pipe_symbol(self): - self.shift() - self.namespace.pipe_symbol = self.get_arg('missing pipe symbol') - - def _check_pipe_symbol(self): - if not self.isvalid(self.namespace.pipe_symbol): - self.error('IndexError', 'invalid pipe symbol type', self._symbols) - def _set_pipe(self, arg): if '-p' == arg or '--pipe-to-file' == arg: self._set_pipe_flag() self._set_pipe_file() - self._set_pipe_symbol() - self._check_pipe_symbol() class Parser(SimpleHelpParser, SimpleOptParser, SimpleArgParser): @@ -190,15 +184,12 @@ def __init__(self, args, progname=None): '-h', '--help', '-p', '--pipe-to-file' ] - # &> and & are synomynous - self._symbols = ['>', '>>', '&>', '&'] self._progname = progname or args[0] self.index = int() self.args = args[1:] self.namespace = Namespace() self.namespace.progname = self._progname self.namespace.pipe = bool() - self.namespace.pipe_symbol = '>' # default is truncate on stdout (universal) self.namespace.pipe_file = None self.namespace.interpreter = None self.namespace.script = None @@ -244,5 +235,5 @@ def parse_args(self): namespace = vars(parser.get_namespace()) - for key, value in namespace.iteritems(): + for key, value in namespace.items(): print('{} = {}'.format(key, value)) diff --git a/cp/main.py b/cp/main.py index 3db4d37..327cceb 100644 --- a/cp/main.py +++ b/cp/main.py @@ -25,6 +25,7 @@ # just remove the filemode argument from the basicConfig call. # by default, filemode is already set to append. # +from __future__ import absolute_import from __future__ import print_function from sys import argv, platform from logging import basicConfig, DEBUG, info @@ -49,7 +50,7 @@ command = cplib.set_command(namespace) - code, time = cplib.set_clock(command) + code, time = cplib.set_clock(command, namespace, 'w') cplib.print_clock(code, time) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index fb8dddc..92cd28f 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -70,7 +70,7 @@ function run(command, cp) { let info = path.parse(file.path); let extensions = atom.config.get('atom-python-run.extension_filter'); let args = command.split(' '); - let shell = new terminal.Shell(); + let shell = new terminal.Shell(__dirname + '/../cp/main.py'); let spawn = new terminal.SpawnWrapper(shell.object); function invalid(extensions) { diff --git a/lib/terminal.js b/lib/terminal.js index a5a35e3..961ba9a 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -5,24 +5,6 @@ const spawn = require('child_process').spawn; -/* - ########## - Base -- this class is the base class for all other classes - ########## - A base object is used in the construction of other objects. - - The object is named 'object' and stored within the constructed object as a - property. - - This property can be accessed by using the get() and set() methods. - - The object property can be accessed directly as well for convenience and is - named object for generic reference purposes because the name of an object - is indeterminate. - - The isEmpty() method can be used to figure out whether the object contains - a value or not. -*/ class Base { constructor(base, ...source){ this.set(base, ...source) @@ -53,22 +35,6 @@ class Base { } -/* - ########## - LinuxSessionWrapper -- determine the default shell for the given desktop environment - ########## - This object determines the default shell, and the default shell option, by - determining the users current desktop environment (or session). - - By default, once a LinuxSessionWrapper object has be created, it will - contain the default values which are appropriate for that Desktop Environment. - - This is only for linux because linux has a variety of desktop environments - available. - - If another shell is used, this can be modified later during instantiation of - a Shell object by modifying the shell and option property values. -*/ class LinuxSessionWrapper extends Base { constructor() { super(); @@ -136,43 +102,13 @@ class LinuxSessionWrapper extends Base { return "-x"; case 'konsole': case 'lxterminal': - case 'xterm': - return "-e"; default: - return null; + return "-e"; } } }; -/* - ########## - MetaShell -- a class used to create meta data for a terminal instance - ########## - This is a blueprint for the Shell class. - - MetaShell is a special meta class that helps automate the constuction of - metadata used to create a terminal instance. - - These objects are used to construct Shell instance objects utilized by the - instantiated terminal object. - - While this object can be changed once instantiated by utilizing the - setBase() method, it's typically best to pass any arguments to the - constructor instead. - - All MetaShell objects have 5 properties: - - shell The terminal instance to be created - - option The terminal instance execution option - - call A REPL or Interpreter that executes 'script' - - script A program to execute given arguments within a 'shell' - - command The commands to be executed by 'script' (Mac OS X Only) - - i.e. : - $ shell option call script - translates to - $ gnome-terminal -x python script.py -*/ class MetaShell extends Base { constructor(base, ...source) { super(); @@ -209,8 +145,8 @@ class MetaShell extends Base { setCommand() { let object = this.get(); this.set(object, { - 'command': function (...args) { - let path = object.call + " " + object.script; + 'command': function (args) { + let path = `${object.call} ${object.script}`; for (let token of args) { path += ` ${token}`; } @@ -221,22 +157,6 @@ class MetaShell extends Base { } -/* - ########## - Shell -- a class used to create meta data for a terminal instance - ########## - The Shell class creates a Shell object according to its local environment. - - A 'path' can be passed to the constructor, appropriate method according to - the local environment, or by manually invoking the setUp() method which - determines the local environment automatically. - - The Shell object is used to define the metadata that is passed to a - SpawnWrapper instance call which is then used to create a . - - 'path' should always be a string. If 'path' is undefined, a default path - is automatically set instead. -*/ class Shell extends Base { constructor(path) { super(); @@ -274,11 +194,11 @@ class Shell extends Base { setUp(path) { switch (process.platform) { case 'win32': - return this.win32(path).get(); + return this.win32(path).object; case 'darwin': - return this.darwin(path).get(); + return this.darwin(path).object; case 'linux': - return this.linux(path).get(); + return this.linux(path).object; default: return null; } @@ -286,31 +206,6 @@ class Shell extends Base { } -/* - ########## - Log -- automate the logging process while executing in atom. - ########## - Create a log object to capture and print basic i/o operations to - standard output. - - A Log call will require an object during instantiation with the following - format: - - tty Requires a instance - - options The options object passed to spawn() - - args The parameters to be executed by the terminal instance - - cp A boolean value that determines a terminal instance call - - meta The metadata to be passed to a terminal instance call - - The 'meta' property should contain a Shell instance object because the - metaData() method expects specific properties to be available. - - This object should be used ONLY after spawn has executed and a tty instance - has been created. - - NOTE: - At least one key/value pair is required to create a valid Log instance, but - will ultimately fail once the call() method is executed. -*/ class Log extends Base { constructor(base, ...source) { super(); @@ -356,35 +251,6 @@ class Log extends Base { } -/* - ########## - SpawnWrapper -- creates a spawn wrapper object - ########## - SpawnWrapper class creates a child_process.spawn() wrapper object. - - A Shell.object is passed to SpawnWrapper during instantiation. - - The Shell.tty() method is used to create a object. - - The tty() method takes a single object called 'model' which is a psuedo - representation of how the object should be created. - - log A flag based Boolean value - - cp Ditto... - - args An array of arguments to be executed by a terminal instance - - options The 'options' object passed to child_process.spawn() - - The object passed to this wrapper is the Shell object. The instance of the Shell - object is used to determine the call that is made to the child_process.spawn method. - - Once a wrapper instance has been instantiated, the only method that should be called - is the tty() method. - - SpawnWrapper.tty() will return the active object after execution. - - NOTE: - Even though this object requires a Shell instance object to instantiate itself, - this process is mainly automated. -*/ class SpawnWrapper extends Base { constructor(base, ...source) { super(); @@ -465,9 +331,9 @@ class SpawnWrapper extends Base { } -// exports.Base = Base; -// exports.LinuxSessionWrapper = LinuxSessionWrapper; -// exports.MetaShell = MetaShell; -// exports.Log = Log; +exports.Base = Base; +exports.LinuxSessionWrapper = LinuxSessionWrapper; +exports.MetaShell = MetaShell; exports.Shell = Shell; +exports.Log = Log; exports.SpawnWrapper = SpawnWrapper; From 47b3c2f1f8c06e41a40543a4b123060e29174941 Mon Sep 17 00:00:00 2001 From: jargon Date: Tue, 5 Sep 2017 20:14:15 -0400 Subject: [PATCH 47/77] minor cosmetic fix... --- lib/atom-python-run.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index 92cd28f..c85e30b 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -82,7 +82,9 @@ function run(command, cp) { function parse(array) { function format(string, object) { - return string.replace(/{.*?}/g, element => object[element.substring(1, element.length - 1)]); + return string.replace(/{.*?}/g, element => + object[element.substring(1, element.length - 1)] + ); } array.forEach((item, index, arr) => { arr[index] = format(item, { From 4d055ece702f689986fc86054f65721a94a5f16f Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Sun, 24 Sep 2017 23:36:01 -0400 Subject: [PATCH 48/77] refactored cp and fixed major bugs and issues --- cp/cp/cplib.py | 108 ------------ cp/cp/fnp.py | 339 +++++++++++++++++++++++++++++++++++++ cp/cp/lexer.py | 367 +++++++++++++++++++++++++++++++++++++++++ cp/cp/parse.py | 357 ++++++++++++++------------------------- cp/main.py | 34 ++-- lib/atom-python-run.js | 11 +- 6 files changed, 867 insertions(+), 349 deletions(-) delete mode 100644 cp/cp/cplib.py create mode 100644 cp/cp/fnp.py create mode 100644 cp/cp/lexer.py diff --git a/cp/cp/cplib.py b/cp/cp/cplib.py deleted file mode 100644 index abe84c6..0000000 --- a/cp/cp/cplib.py +++ /dev/null @@ -1,108 +0,0 @@ -# -# NOTE: 2to3 and 3to2 Compatability -# importing should consider 2to3 and 3to2 implications. -# -# https://stackoverflow.com/questions/85451/python-time-clock-vs-time-time-accuracy#85533 -# https://docs.python.org/3/library/time.html#time.time -# https://docs.python.org/2.7/library/time.html#time.clock -# https://docs.python.org/2/library/timeit.html -# https://docs.python.org/2/library/sys.html#sys.platform -# https://docs.python.org/2/library/string.html#formatstrings -# -from __future__ import absolute_import -from __future__ import print_function -from os import system, environ -from os.path import isdir, dirname -from subprocess import call -from time import time -from sys import platform -import logging -from .parse import Parser - - -__all__ = ( - 'set_log_path', 'set_namespace', 'set_command', - 'set_clock', 'print_clock', - 'pause' -) - - -version_info = "v0.1.1" - - -# -# NOTE: -# an improved methodology for determining the defaul log path is needed -# -def set_log_path(): - if 'win32' == platform: - log = "{}\\.atom\\packages\\atom-python-run\\cp.log".format(environ['USERPROFILE']) - else: - log = "{}/.atom/packages/atom-python-run/cp.log".format(environ['HOME']) - path = dirname(log) - if not isdir(path): - return "cp.log" - return log - - -def set_namespace(args): - parser = Parser(args) - parser.check_args(args) - parser.parse_opts() - parser.parse_args() - namespace = parser.get_namespace() - for key, value in vars(namespace).items(): - logging.debug('namespace: key "%s": value "%s"', key, value) - return namespace - - -def set_command(namespace): - command = list() - command.append(namespace.interpreter) - for opt in namespace.options: - command.append(opt) - command.append(namespace.script) - if namespace.pipe: - command.append(namespace.pipe_file) - logging.debug('command: %s', command) - return command - - -def set_call(command, namespace, mode): - code = None - if namespace.pipe: - with open(namespace.pipe_file, mode) as f: - code = call(command, stdout=f, stderr=f) - f.close() - else: - code = call(command) - return code - - -def log_clock(code, clock): - logging.info('return code: %d (0x%x)', code, code) - logging.info('elapsed time: %.6f', clock) - - -def set_clock(command, namespace=None, mode='a'): - clock = time() - code = set_call(command, namespace, mode) - clock = time() - clock - log_clock(code, clock) - return code, clock - - -def print_clock(code, clock): - print("\nProcess returned {:d} (0x{:x})".format(code, code), end="\t") - print("execution time : {:.3f} s".format(clock)) - - -def pause(): - if 'win32' == platform: - system('pause') - elif 'darwin' == platform: - system('echo "Close this window to continue..."') - elif 'linux' == platform[:-1]: - system("printf 'Press [ENTER] to continue...'; read _;") - else: - logging.info('Unknown OS Type: %s', platform) diff --git a/cp/cp/fnp.py b/cp/cp/fnp.py new file mode 100644 index 0000000..5711d79 --- /dev/null +++ b/cp/cp/fnp.py @@ -0,0 +1,339 @@ +"""fnp - discern whether the given string is a valid file name and/or path.""" +# +# ###### +# NOTE # +######## +# Some people, when confronted with a problem, think +# "I know, I'll use regular expressions." Now they have two problems. +# +# I find nothing wrong with using regular expressions when appropriate. In the end, +# I chose "structured code" over "regex" for "precision and speed". +# +# This module seems to be the "better" choice overall for this single use case. +# +# I came to this conclusion after some time, consideration, and extensive testing. +# +# Typically, fragile code is bad code; in this case it couldn't be helped. +# Any desired changes or fixes could impact other portions of the code. +# The major trade off seemed to be between "code complexity" and "execution speed". +# This module runs more "accurately" and "effeciently" than using a module fitted for regex. +# +# I needed a single use function for determining whether or not a given string +# could pass as a "valid" file name and/or a "valid" path to a file name. +# +# This module checks a given string and determines whether or not it is a valid file path. +# It does not (nor should it) determine a files creditablity or accessability. +# +# NOTE: The file need not exist nor be created. It should return True on any +# path considered "valid" as long as it is not pointing to a directory. +# +# ################ +# Absolute Paths # +# ################ +# : ]- Current Directory --| +# |-- Windows Only - requires alpha prefix +# :\ ]- Root Directory --| +# +# / ]- Root Directory |-- *nix Only - required prefix for absolute path +# +# ################ +# Relative Paths # +# ################ +# Relative paths are universal (for the most part) +# +# Windows allows a '/' or a '\' as long as a single form is used +# Unix requires a '/' and uses the '\' character as an escape character +# +# . .. ./ .\ ../ ..\ ]-- all valid relative path prefixes +# +# ################# +# Path Seperators # +# ################# +# whether or not any valid prefix is correct is system dependent. +# this implementation is system dependent and not all forms are considered valid. +# +# i.e.: the following command line is valid on a win32 system +# $ cd C:/Users/Username/Documents +# +# NOTE: the this will fail when this module is used because the seperator value is defined +# by the os.path.sep variable and then used to validate the given path like patterns. +# +# this module would only consider C:\Users\Username\Documents to be valid +# +from __future__ import print_function +from sys import platform +from os.path import sep, isdir +from string import ascii_letters, digits + +__all__ = ('FileNamePattern', 'filenames', 'winpaths', 'nixpaths') + + +class Base(object): + def __init__(self, charset=None): + self._charset = charset + self._sep = sep + self._legalPathSet = [":", ":\\", "/", "./", ".\\", "../", "..\\"] + self._legalCharSet = ascii_letters + digits + '_.' + + def getCharSet(self): + return self._charset + + def setCharSet(self, charset): + self._charset = charset + + +class Grammar(object): + # requires exception since value is unknown + def hasLegalPath(self, start, stop=None): + try: + if stop is None: + return self._charset[start] in self._legalPathSet + return self._charset[start:stop] in self._legalPathSet + except IndexError: + return False + + # requires exception since value is unknown + def hasLegalChar(self, position): + try: + return self._charset[position] in self._legalCharSet + except IndexError: + return False + + def nextCharIsLegal(self, position): + return self.hasLegalChar(position + 1) + + # NOTE: will always return an empty string on failure + def endsWithPathSep(self): # this block is here for testing purposes + return self._charset[-1:] == self._sep + + # requires exception since value is unknown + def startsWithChar(self): + try: + return self._charset[0].isalpha() + except IndexError: + return False + + def startsWithLegalChar(self): + return self.hasLegalChar(0) + + def startsWithPathSep(self): + return self.hasLegalPath(0) and self._charset[0] == self._sep + + def startsWithCurrentDir(self): + return self.hasLegalPath(0, 2) and self._charset[0] == '.' + + def startsWithParentDir(self): + return self.hasLegalPath(0, 3) and self._charset[0] == '.' + + def startsWithWinCurrentPath(self): + return self.startsWithChar() and self.hasLegalPath(1) \ + and self.hasLegalChar(2) + + def startsWithWinDrivePath(self): + return self.startsWithChar() and self.hasLegalPath(1, 3) + + +class Scanner(object): + def pathIsRelative(self): + return self.startsWithCurrentDir() or self.startsWithParentDir() + + def winAbsolutePath(self): + return self.startsWithWinCurrentPath() or self.startsWithWinDrivePath() + + def nixAbsolutePath(self): + return '/' == self._charset[0] + + def pathIsAbsolute(self): + return self.winAbsolutePath() or self.nixAbsolutePath() + + def pathIsLegal(self): + for pos, char in enumerate(self._charset): + if (char == self._sep or self.startsWithWinCurrentPath()) and self.nextCharIsLegal(pos): + return True + return False + + def pathIsValid(self): + if self.pathIsRelative() or self.pathIsAbsolute(): + return self.pathIsLegal() + return self.startsWithLegalChar() and self.nextCharIsLegal(0) + + +class FileNamePattern(Base, Grammar, Scanner): + def testHasFileName(self): + if self.endsWithPathSep(): + return False + return self.pathIsValid() + + def testDisplayPath(self, label, path): + print("{}:".format(label), "{}:".format(self.testHasFileName()), path) + + def hasFileName(self): + if isdir(self._charset): + return False + return self.pathIsValid() + + def displayPath(self, label, path): + print("{}:".format(label), "{}:".format(self.hasFileName()), path) + + +def main(): + fnp = FileNamePattern() + if platform == 'win32': + pathlist = filenames + winpaths + else: + pathlist = filenames + nixpaths + print("Label: Valid file name: File path") + for p in pathlist: + fnp.setCharSet(p) + fnp.testDisplayPath('Path', p) + + +filenames = [ + "filename", + ".filename", + "_filename", + "file name", + ".file name", + "_file name", + "filename.ext", + ".filename.ext", + "_filename.ext", + "file name.ext", + ".file name.ext", + "_file name.ext" +] + +winpaths = [ + "\\", + "\\.", + "\\_", + "\\home", + "\\home\\", + "\\home\\username", + "\\home\\username\\some dir", + "\\home\\username\\some dir\\", + "\\home\\username\\some dir\\path", + "home\\username\\some dir\\path", + "home\\username\\some dir\\path/", + ".home\\username\\some dir\\path", + ".home\\username\\some dir\\path\\", + "..home\\username\\some dir\\path", + "..home\\username\\some dir\\path\\", + "\\home\\username\\some dir\\path\\.", + "\\home\\username\\some dir\\path\\_", + "\\home\\username\\some dir\\path\\.filename", + "\\home\\username\\some dir\\path\\_filename", + "\\home\\username\\some dir\\path\\filename", + ".", + ".\\", + "..\\", + ".\\.", + "..\\.", + ".\\_", + "..\\_", + ".\\.filename", + "..\\.filename.ext", + ".\\_filename", + "..\\_filename.ext", + ".\\filename", + "..\\filename.ext", + ".\\Users", + "..\\Users\\Username", + ".\\Users\\Username\\", + ".\\Users\\Username\\Some file", + "..\\Users\\Username\\Some file\\", + ".\\Users\\Username\\Some file\\path", + "..\\Users\\Username\\Some file\\.path", + "...\\Users\\Username\\Some file\\_path", + "...\\Users\\Username\\Some file\\path\\filename.ext", + "..\\Users\\Username\\Some file\\.path\\filename", + ".\\Users\\Username\\Some file\\_path\\filename.ext", + "C", + "C:", + "C:", + "C:.", + "C:_", + "C:+", + "C:Users", + "C:Users\\Username", + "C:Users\\Username\\", + "C:Users\\Username\\Some file", + "C:Users\\Username\\Some file\\", + "C:Users\\Username\\Some file\\path", + "C:Users\\Username\\Some file\\.path", + "C:Users\\Username\\Some file\\_path", + "C:Users\\Username\\Some file\\path\\filename.ext", + "C:Users\\Username\\Some file\\.path\\filename", + "C:Users\\Username\\Some file\\_path\\filename.ext", + "C", + "C:", + "C:\\", + "C:\\.", + "C:\\_", + "C:\\+", + "C:\\Users", + "C:\\Users\\Username", + "C:\\Users\\Username\\", + "C:\\Users\\Username\\Some file", + "C:\\Users\\Username\\Some file\\", + "C:\\Users\\Username\\Some file\\path", + "C:\\Users\\Username\\Some file\\.path", + "C:\\Users\\Username\\Some file\\_path", + "C:\\Users\\Username\\Some file\\path\\filename.ext", + "C:\\Users\\Username\\Some file\\.path\\filename", + "C:\\Users\\Username\\Some file\\_path\\filename.ext", +] + +nixpaths = [ + ".", + "./", + "../", + "./.", + "../.", + "./_", + "../_", + "./.filename", + "../.filename.ext", + "./_filename", + "../_filename.ext", + "./filename", + "../filename.ext", + "./somedir/.filename", + "../somedir/.filename.ext", + "./somedir/_filename", + "../some/dir/path and name/_filename.ext", + "./some/dir/path and name/filename", + "../some/dir/path and name/filename.ext", + "/", + "/.", + "/_", + "/.filename", + "/.filename.ext", + "/_filename", + "/_filename.ext", + "/home", + "/home/", + "/home/username", + "/home/username/some dir", + "/home/username/some dir/", + "/home/username/some dir/path", + "home/username/some dir/path", + "home/username/some dir/path/", + ".home/username/some dir/path", + ".home/username/some dir/path/", + "..home/username/some dir/path", + "..home/username/some dir/path/", + "/home/username/some dir/path/.", + "/home/username/some dir/path/_", + "/home/username/some dir/path/.filename", + "/home/username/some dir/path/.filenameext", + "/home/username/some dir/path/_filename", + "/home/username/some dir/path/_filename.ext", + "/home/username/some dir/path/filename", + "/home/username/some dir/path/file name", + "/home/username/some dir/path/filename.ext", + "/home/username/some dir/path/file name.ext" +] + +if __name__ == '__main__': + main() diff --git a/cp/cp/lexer.py b/cp/cp/lexer.py new file mode 100644 index 0000000..d8de835 --- /dev/null +++ b/cp/cp/lexer.py @@ -0,0 +1,367 @@ +"""lexer - structures and tests the argv token set as an AST for parsing.""" +# NOTE: use the main.py file instead if you want to execute scripts. +# +############ +# lexer.py # +############ +# while lexer.py is executable, it executes as a dummy file and doesn't actually +# execute anything directly. it simply demonstrates how lexer.py handles the +# "argv" variable. +# +# "argv" is a list and join could be used in conjunction to create a concatenated +# string. +# +# this string could be passed directly to command. it is up to the end user to +# make sure the command executes as intended. +# +############# +# Execution # +############# +# lexer.py is mainly designed to be an extremely lightweight check. +# +# it's rules restrict what can be passed to it while remaining flexible enough +# to be able achieve arbitrary execution. +# +# in example: +# +# this will always fail... and should always fail. +# -> "python" is the interpreter to be executed by lexer.py +# -> "-c" is the option to be passed to python (not lexer.py) +# -> "string" is the command to be executed by python (not lexer.py) +# +# the point of the file check is to determine if the file actually exists; not +# to see if it is executable. we also have no intention of writing to it. +# obviously, a string is executable, but we want to execute a file; not a string. +# +# $ python lexer.py python -c 'print("hello, world!")' +# Error: Expected optional option argument: got "'print(hello, world!)'" instead +# +# $ python -c "print('Hello, World!')" +# bash: !': event not found +# +# $ python -c 'print("hello, world!")' +# hello, world +# +############# +# Expansion # +############# +# NOTE: the shell should handle user and variable expansion it self. user, +# variable, and glob expansion should never occur. +# +# the final argument should always be a source file that can be executed by the +# given interpreter. +# +# $ python lexer.py node ~/documents/js/hello.js +# ...dictionary output here... +# +########### +# Options # +########### +# NOTE: lexer.py will handle its own options as long as they are placed before +# the interpreter argument. +# +# any arguments inbetween the interpreter and the filename are considered +# options to be passed to the interpreter itself. +# +# lexer.py also handles redirection. for example, if you want to pipe stdout and +# stderr to a file, you can do this +# +# $ python lexer.py -f out.log node ~/documents/js/hello.js +# ...dictionary output here... +# +# for more usage information, do +# +# $ python lexer.py -h +# ...help output here... +# +# an example on how to use the Lexer object is demonstrated in a function named +# "main()" at the end of the file. +# +from __future__ import print_function +from __future__ import absolute_import +from sys import argv +import os.path +import logging +from .fnp import FileNamePattern + + +__all__ = ("Lexer", "Scanner") + + +class BarePhraseStructure(object): + pass + + +class Namespace(object): + def __init__(self, args, progname=None): + self._opts = [ + '-h', '--help', + '-p', '--pause', + '-f', '--file' + ] + + self._optArgs = [ + '-f', '--file' + ] + + self._index = int() + self._error = str() + self._ast = args[1:] + + self._namespace = BarePhraseStructure() + self._namespace.name = progname or args[0] + self._namespace.pause = bool() + self._namespace.file = bool() + self._namespace.path = str() + self._namespace.repl = str() + self._namespace.options = list() + self._namespace.script = str() + self._namespace.last = args[-1:][0] + + try: + self.arg = args[1:][0] + except IndexError: + self.usage() + exit(1) + + self.logAst() + self.logToken() + + # Namespace + def getNamespace(self): + return vars(self._namespace) + + def printNamespace(self): + for key, value in self.getNamespace().items(): + print('{}: {}'.format(key, value)) + + +class Log(Namespace): + def logNamespace(self): + for key, value in self.getNamespace().items(): + logging.debug('namespace: [key] "%s" [value] "%s"', key, value) + + def logToken(self): + logging.debug('Index: "%s" Token: "%s"', self._index, self.arg) + + def logError(self): + logging.error(self._error) + + def logAst(self): + logging.debug('_ast: "%s"', self._ast) + + +class Error(Namespace): + def setError(self, taxonomy, error, message=None): + if message: + self._error = '{}: {}: {}'.format(taxonomy, error, message) + else: + self._error = '{}: {}'.format(taxonomy, error) + + def getError(self): + return self._error + + def error(self, taxonomy, error, message=None): + self.setError(taxonomy, error, message) + self.logError() + print(self._error) + exit(1) + + +class Token(Namespace): + def shift(self): + self._index += 1 + + def setToken(self, error=None, message=None): + try: + self.arg = self._ast[self._index] + except IndexError: + self.error('Error', error, message) + + def shiftToken(self, error=None, message=None): + self.shift() + self.setToken(error, message) + self.logToken() + + def getToken(self): + return self.arg + + def printToken(self): + print(self.arg) + + +class Help(Namespace): + def usage(self): + print('Usage: {} [-h] [-p] [-f [filename]] repl args [args...]'.format(self._namespace.name)) + + def summary(self): + print('{} - handles arbitrary arguments to be executed.\n'.format(self._namespace.name)) + print('Usage\n-----\n\t{} [-h] [-p] [-f [filename]] repl args [args...]'.format(self._namespace.name)) + print( + "\n" + "Options\n" + "-------\n" + "\t-h\t--help\t\tprints this help text\n" + "\t-f\t--file\t\tpipes 'stdout' and 'stderr' to the given file\n" + "\t-p\t--pause\t\tprompt client for 'stdin'\n" + "\n" + "Parameters\n" + "----------\n" + "\trepl\t\tthe repl used to execute 'script'\n" + "\tscript\t\t\tthe file to be executed by 'repl'\n" + "\n" + ) + print('Example: {} -p -f output.log python -i source.py'.format(self._namespace.name)) + + +class Grammar(Log, Error, Token, Help): + def hasOption(self): + return '-' == self.arg[0] + + def hasValidOption(self): + return self.arg in self._opts + + def hasOptionArgument(self): + return self.arg in self._optArgs + + def hasOptionEnd(self): + return 1 == len(self.arg) + + def endsOptionScan(self): + return self.hasOptionEnd() if self.hasOption() else not self.hasOption() + + def hasHelpOption(self): + return '-h' == self.arg or '--help' == self.arg + + def hasPauseOption(self): + return '-p' == self.arg or '--pause' == self.arg + + def hasFileOption(self): + return '-f' == self.arg or '--file' == self.arg + + def hasScript(self): # TOCTOU - needs a better way + return os.path.isfile(self.arg) and os.path.isfile(self._namespace.last) + + def hasRepl(self): + return not self.hasOption() and not self.hasScript() and self.arg + + def hasValidFileName(self): + fnp = FileNamePattern(self.arg) + return fnp.hasFileName() + + def _setHelpOption(self): + if self.hasHelpOption(): + self.summary() + exit(0) + + def _setPauseOption(self): + if self.hasPauseOption(): + self._namespace.pause = True + + def _setFileOption(self): + if self.hasFileOption(): + self._namespace.file = True + + def _setFilePath(self): + if self.hasOptionArgument(): + self.shiftToken('Missing required file path for option "{}"'.format(self.arg)) + self._exitOnInvalidFilename() + self._namespace.path = self.arg + + def _setScannedOption(self): + if self.hasValidOption(): + self._setHelpOption() + self._setPauseOption() + self._setFileOption() + self._setFilePath() + self.shiftToken( + 'Expected required repl argument', + 'got "{}" instead'.format(self.arg) + ) + + def _exitOnInvalidFilename(self): + if not self.hasValidFileName(): + self.error( + 'Error', + 'Expected required option argument "filename"', + 'got "{}" instead'.format(self.arg) + ) + + def _exitOnInvalidOption(self): + if not self.hasValidOption(): + self.error( + 'Error', + 'Expected optional argument "option"', + 'got "{}" instead'.format(self.arg) + ) + + def _exitOnInvalidRepl(self): + if not self.hasRepl(): + self.error( + 'Error', + 'Expected required argument "repl"', + 'got "{}" instead'.format(self.arg) + ) + + def _exitOnMissingScript(self): + if not self.hasScript(): + self.error( + 'Error', + 'Expected required argument "script"', + 'got "{}" instead'.format(self.arg) + ) + + +class Scanner(Grammar): + def scanOptions(self): + while not self.endsOptionScan(): + self._exitOnInvalidOption() + self._setScannedOption() + + def scanRepl(self): + self._exitOnInvalidRepl() + self._namespace.repl = self.arg + self.shiftToken( + 'Expected required script argument', + 'got "{}" instead'.format(self.arg) + ) + + def scanArguments(self): + while not self.hasScript(): + self._namespace.options.append(self.arg) + self.shiftToken( + 'Expected optional option argument', + 'got "{}" instead'.format(self.arg) + ) + + def scanScript(self): + self._exitOnMissingScript() + self._namespace.script = self.arg + + def scan(self): + self.scanOptions() + self.scanRepl() + self.scanArguments() + self.scanScript() + + +class Lexer(Scanner): + pass + + +def main(ast): + lexer = Lexer(ast) + lexer.scan() + lexer.logNamespace() + lexer.printNamespace() + + +if __name__ == '__main__': + logging.basicConfig( + filename='testing-cp.log', + filemode='w', + level=logging.DEBUG, + format='%(levelname)s: %(message)s' + ) + + main(argv) diff --git a/cp/cp/parse.py b/cp/cp/parse.py index ad45ed6..2f9a243 100644 --- a/cp/cp/parse.py +++ b/cp/cp/parse.py @@ -1,239 +1,136 @@ """parser - handles arbitrary arguments to be executed.""" -# NOTE: use the main.py file instead if you want to execute scripts. -# while parse.py is executable, it executes as a dummy file and doesn't actually -# execute anything directly. it simply demonstrates how parse.py handles the -# argv variable. # -# argv is a list and join could be used in conjunction to create a concatenated -# string. +# NOTE: 2to3 and 3to2 Compatability +# importing should consider 2to3 and 3to2 implications. # -# this string could be passed directly to command. it is up to the end user to -# make sure the command executes as intended. -# -# parse.py is mainly designed to be an extremely lightweight check. -# -# it's rules restrict what can be passed to it while remaining flexible enough -# to be able achieve arbitrary execution. -# -# in example: -# -# this will always fail... and should always fail. -# -> python is the interpreter to be executed by parse.py -# -> -c is the option to be passed to python (not parse.py) -# -> string is the command to be executed by python (not parse.py) -# -# the point of the file check is to determine if the file actually exists before -# attempting to read it; not to see if it is executable. we also have no -# intention of writing to it. obviously, a string is executable, but we want -# to execute a file; not a string. -# -# $ python parse.py python -c 'print("hello, world!")' -# IOError: file does not exist: print("hello, world!") -# -# $ python -c "print('Hello, World!')" -# bash: !': event not found -# -# $ python -c 'print("hello, world!")' -# hello, worlds -# -# parse.py will handle user and variable expansion. -# globs should never be expanded. -# the final argument should always be a source file that can be executed by the -# given interpreter. -# -# $ python parse.py node ~/documents/js/hello.js -# ...dictionary output here... -# -# parse.py will handle its own options as long as they are placed before the -# interpreter argument. -# any arguments inbetween the interpreter and the filename are considered -# options to be passed to the interpreter itself. -# -# parse.py also handles redirection. for example, if you want to pipe stdout -# while appending to a file, you can do this -# -# $ python parse.py -p out.log '>>' node ~/documents/js/hello.js -# ...dictionary output here... -# -# for more usage information, do -# -# $ python parse.py -h -# ...help output here... -# -# an example on how to use the Parser object is demonstrated at the end of -# the file +# https://stackoverflow.com/questions/85451/python-time-clock-vs-time-time-accuracy#85533 +# https://docs.python.org/3/library/time.html#time.time +# https://docs.python.org/2.7/library/time.html#time.clock +# https://docs.python.org/2/library/timeit.html +# https://docs.python.org/2/library/sys.html#sys.platform +# https://docs.python.org/2/library/string.html#formatstrings # +from __future__ import absolute_import from __future__ import print_function -from sys import argv -from os.path import isfile, expanduser, expandvars +from os import system, environ +from os.path import isdir, dirname +from subprocess import call +from time import time +from sys import platform import logging +from .lexer import Lexer + + +__all__ = ("Parser",) + + +class Base(object): + def __init__(self): + self._lexer = None + self._namespace = dict() + self._command = list() + self._logPath = str() + self._exitCode = None + self._clock = None + + def setLexer(self, ast): + self._lexer = Lexer(ast) + self._lexer.scan() + self._lexer.logNamespace() + def getLexer(self): + return self._lexer -__all__ = ("Parser") - - -class Namespace(object): - pass - - -class SimpleHelpParser(Namespace): - def usage(self): - print('Usage: {} [-h] [-p [filename] [symbol]] interpreter args [args]...'.format(self._progname)) - - def summary(self): - print('{} - handles arbitrary arguments to be executed.\n'.format(self._progname)) - print('Usage\n-----\n\t{} [-h] [-p [filename] [symbol]] interpreter args [args]...'.format(self._progname)) - print( - "\n" - "Options\n" - "-------\n" - "\t-h\t--help\t\t\tprints this help text\n" - "\t-p\t--pipe-to-file\t\tpipes stdout and stderr to the given filename\n" - "\n" - "Parameters\n" - "----------\n" - "\tinterpreter\t\tthe interpreter used to execute script\n" - "\tscript\t\t\tthe file to be executed by interpreter\n" - "\n" - "Symbols\n" - "-------\n" - "\t>\t\ttruncate on stdout\n" - "\t>>\t\tappend on stdout\n" - "\t&>\t\ttruncate on stdout and stderr\n" - "\t&\t\tsynonym for &>\n\n" - "Symbols should always be quoted. Consequences could otherwise occur.\n" - ) - print('Example: {} -p output.log "&" python -i source.py'.format(self._progname)) - - -class SimpleArgParser(Namespace): - def shift(self): - self.index += 1 - logging.debug('shift(): index is now %s', self.index) - - def error(self, taxonomy, message, extendmsg=None): - if not extendmsg: - message = '{}: {}'.format(taxonomy, message) + def setNamespace(self): + self._namespace = self._lexer.getNamespace() + + def getNamespace(self): + return self._namespace + + +class Command(Base): + def _setRepl(self): + self._command.append(self._namespace['repl']) + + def _setOptions(self): + for option in self._namespace['options']: + self._command.append(option) + + def _setScript(self): + self._command.append(self._namespace['script']) + + def _logCommand(self): + logging.debug('command: %s', self._command) + + def setCommand(self): + self._setRepl() + self._setOptions() + self._setScript() + self._logCommand() + + def getCommand(self): + return self._command + + +class Clock(Base): + def getClock(self): + return self._clock + + def logClock(self): + logging.info('return code: %d (0x%x)', self._exitCode, self._exitCode) + logging.info('elapsed time: %.6f', self._clock) + + def printClock(self): + print("\nProcess returned {:d} (0x{:x})".format( + self._exitCode, self._exitCode), end="\t") + print("execution time : {:.3f} s".format(self._clock)) + + +class Call(Base): + def pipeCall(self, mode='a'): + with open(self._namespace['path'], mode) as f: + self._clock = time() + self._exitCode = call(self._command, stdout=f, stderr=f) + self._clock = time() - self._clock + f.close() + + def call(self, mode='a'): + self._clock = time() + self._exitCode = call(self._command) + self._clock = time() - self._clock + + def getExitCode(self): + return self._exitCode + + +class Logpath(Base): + def hasLogPath(self): + return isdir(dirname(self._logPath)) + + def defaultLogPath(self): + if 'win' in platform: + return "{}\\.atom\\packages\\atom-python-run\\cp.log".format(environ['USERPROFILE']) + return "{}/.atom/packages/atom-python-run/cp.log".format(environ['HOME']) + + def setLogPath(self, path=None): + if path is None: + self._logPath = self.defaultLogPath() + else: + self._logPath = path + + def getLogPath(self): + if self.hasLogPath(): + return self._logPath + return "cp.log" + + +class Parser(Command, Call, Clock, Logpath): + def pause(self): + if 'win' in platform: + system('pause') + elif 'darwin' in platform: + system('echo "Close this window to continue..."') + elif 'linux' in platform: + system("printf 'Press [ENTER] to continue...'; read _;") else: - message = '{}: {}: {}'.format(taxonomy, message, extendmsg) - print(message) - logging.error(message) - exit(1) - - def get_arg(self, errmsg=None): - try: - arg = self.args[self.index] - logging.debug('get_arg(): current argument: %s', arg) - return arg - except IndexError as e: - if errmsg: - message = 'IndexError: {}: {}'.format(errmsg, e) - else: - message = 'IndexError: {}'.format(e) - print(message) - logging.error(message) - exit(1) - - def _set_interpreter(self): - if self.namespace.pipe: - self.shift() - self.namespace.interpreter = self.get_arg('missing interpreter') - - def _set_options(self): - while True: - self.shift() - arg = self.get_arg('missing interpreter argument') - if arg == self.namespace.last_element: - break - self.namespace.options.append(arg) - - def _set_script(self): - # last arg should always be the script! - if not isfile(self.namespace.last_element): - self.error('IOError', 'file does not exist', self.namespace.last_element) - self.namespace.script = expanduser(expandvars(self.namespace.last_element)) - - -class SimpleOptParser(Namespace): - def isvalid(self, argument): - if argument in self._options: - return True - return False - - def _help(self, arg): - if '-h' == arg or '--help' == arg: - self.summary() - exit(1) - - def _set_pipe_flag(self): - self.namespace.pipe = True - - def _set_pipe_file(self): - self.shift() - self.namespace.pipe_file = self.get_arg('no file to pipe to') - - def _set_pipe(self, arg): - if '-p' == arg or '--pipe-to-file' == arg: - self._set_pipe_flag() - self._set_pipe_file() - - -class Parser(SimpleHelpParser, SimpleOptParser, SimpleArgParser): - def __init__(self, args, progname=None): - self._options = [ - '-h', '--help', - '-p', '--pipe-to-file' - ] - self._progname = progname or args[0] - self.index = int() - self.args = args[1:] - self.namespace = Namespace() - self.namespace.progname = self._progname - self.namespace.pipe = bool() - self.namespace.pipe_file = None - self.namespace.interpreter = None - self.namespace.script = None - self.namespace.options = list() - self.namespace.last_element = args[-1:][0] - - def check_args(self, args): - for index, arg in enumerate(args): - logging.debug('check_args(): index %d: arg: %s', index, arg) - if not len(args) > 1: - self.usage() - exit(1) - - def get_namespace(self): - return self.namespace - - def parse_opts(self): - arg = self.get_arg() - if self.isvalid(arg): - self._help(arg) - self._set_pipe(arg) - logging.debug('namespace: has pipe: %d', self.namespace.pipe) - - def parse_args(self): - self._set_interpreter() - self._set_options() - self._set_script() - logging.debug('namespace: script: %s', self.namespace.script) - - -if __name__ == '__main__': - logging.basicConfig( - filename='testing-cp.log', - filemode='w', - level=logging.DEBUG, - format='%(levelname)s: %(message)s' - ) - - parser = Parser(argv) - parser.check_args(argv) - parser.parse_opts() - parser.parse_args() - - namespace = vars(parser.get_namespace()) - - for key, value in namespace.items(): - print('{} = {}'.format(key, value)) + logging.info('Unknown OS Type: %s', platform) diff --git a/cp/main.py b/cp/main.py index 327cceb..b2057bd 100644 --- a/cp/main.py +++ b/cp/main.py @@ -29,11 +29,16 @@ from __future__ import print_function from sys import argv, platform from logging import basicConfig, DEBUG, info -from cp import cplib +from cp.parse import Parser +version_info = "v0.2.0" if __name__ == '__main__': - logpath = cplib.set_log_path() + parser = Parser() + + parser.setLogPath() + + logpath = parser.getLogPath() basicConfig( filename=logpath, @@ -42,18 +47,27 @@ format='%(levelname)s: %(message)s' ) - info('OS Type: %s', platform) + info('Platform: %s', platform) + + info('Version Info: %s', version_info) + + parser.setLexer(argv) - info('Version Info: %s', cplib.version_info) + parser.setNamespace() - namespace = cplib.set_namespace(argv) + namespace = parser.getNamespace() - command = cplib.set_command(namespace) + parser.setCommand() - code, time = cplib.set_clock(command, namespace, 'w') + if namespace['file']: + parser.pipeCall('a') + else: + parser.call('a') - cplib.print_clock(code, time) + parser.logClock() - cplib.pause() + if namespace['pause']: + parser.printClock() + parser.pause() - exit(code) + exit(parser.getExitCode()) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index c85e30b..736dd26 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -94,6 +94,7 @@ function run(command, cp) { 'ext': info.ext, }); }); + return array; } editor.save(); @@ -102,8 +103,16 @@ function run(command, cp) { return; } - parse(args); + args = parse(args); + /* + NOTE: 'cp' should always be on! + there should be an option to toggle whether `cp` pauses instead. + by default, `cp` should ALWAYS default to pausing. + when `cp` is set to NOT pause, it should default to writing stdout and + stderr to a file instead. + this will always force the argument tokens to be checked. + */ let tty = spawn.tty({ 'log': true, 'cp': cp, From 5154ccaef77b9bdf2ab98fd945b8926ae2a2b358 Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Mon, 25 Sep 2017 05:15:52 -0400 Subject: [PATCH 49/77] refactored and cleaned up code base. --- .gitignore | 3 + cp/cp/parse.py | 75 +++++++------- cp/main.py | 4 +- lib/atom-python-run.js | 220 +++++++++++++++++++++++------------------ lib/test.js | 2 +- 5 files changed, 168 insertions(+), 136 deletions(-) diff --git a/.gitignore b/.gitignore index 70983ed..efb01eb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,11 @@ build test *.pyc +__pycache__ + *.log .DS_Store + npm-debug.log node_modules diff --git a/cp/cp/parse.py b/cp/cp/parse.py index 2f9a243..a48714c 100644 --- a/cp/cp/parse.py +++ b/cp/cp/parse.py @@ -48,6 +48,44 @@ def getNamespace(self): return self._namespace +class Logpath(Base): + def logLogPath(self): + logging.debug('Log Path: %s', self._logPath) + + def hasLogPath(self): + return isdir(dirname(self._logPath)) + + def defaultLogPath(self): + if 'win' in platform: + return "{}\\.atom\\packages\\atom-python-run\\cp.log".format(environ['USERPROFILE']) + return "{}/.atom/packages/atom-python-run/cp.log".format(environ['HOME']) + + def setLogPath(self, path=None): + if path is None: + self._logPath = self.defaultLogPath() + else: + self._logPath = path + + def getLogPath(self): + if not self.hasLogPath(): + self._logPath = "cp.log" + return self._logPath + + +class Clock(Base): + def getClock(self): + return self._clock + + def logClock(self): + logging.info('return code: %d (0x%x)', self._exitCode, self._exitCode) + logging.info('elapsed time: %.6f', self._clock) + + def printClock(self): + print("\nProcess returned {:d} (0x{:x})".format( + self._exitCode, self._exitCode), end="\t") + print("execution time : {:.3f} s".format(self._clock)) + + class Command(Base): def _setRepl(self): self._command.append(self._namespace['repl']) @@ -72,20 +110,6 @@ def getCommand(self): return self._command -class Clock(Base): - def getClock(self): - return self._clock - - def logClock(self): - logging.info('return code: %d (0x%x)', self._exitCode, self._exitCode) - logging.info('elapsed time: %.6f', self._clock) - - def printClock(self): - print("\nProcess returned {:d} (0x{:x})".format( - self._exitCode, self._exitCode), end="\t") - print("execution time : {:.3f} s".format(self._clock)) - - class Call(Base): def pipeCall(self, mode='a'): with open(self._namespace['path'], mode) as f: @@ -99,31 +123,10 @@ def call(self, mode='a'): self._exitCode = call(self._command) self._clock = time() - self._clock - def getExitCode(self): + def exitCode(self): return self._exitCode -class Logpath(Base): - def hasLogPath(self): - return isdir(dirname(self._logPath)) - - def defaultLogPath(self): - if 'win' in platform: - return "{}\\.atom\\packages\\atom-python-run\\cp.log".format(environ['USERPROFILE']) - return "{}/.atom/packages/atom-python-run/cp.log".format(environ['HOME']) - - def setLogPath(self, path=None): - if path is None: - self._logPath = self.defaultLogPath() - else: - self._logPath = path - - def getLogPath(self): - if self.hasLogPath(): - return self._logPath - return "cp.log" - - class Parser(Command, Call, Clock, Logpath): def pause(self): if 'win' in platform: diff --git a/cp/main.py b/cp/main.py index b2057bd..ac3fe89 100644 --- a/cp/main.py +++ b/cp/main.py @@ -51,6 +51,8 @@ info('Version Info: %s', version_info) + parser.logLogPath() + parser.setLexer(argv) parser.setNamespace() @@ -70,4 +72,4 @@ parser.printClock() parser.pause() - exit(parser.getExitCode()) + exit(parser.exitCode()) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index 736dd26..ef1030c 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -1,127 +1,151 @@ 'use strict'; -const CompositeDisposable = require('atom').CompositeDisposable; -const path = require('path'); -const terminal = require('./terminal'); - - module.exports = { activate() { + const CompositeDisposable = require('atom').CompositeDisposable; + const path = require('path'); + const terminal = require('./terminal'); + this.subscriptions = new CompositeDisposable(); - this.subscriptions.add( - atom.commands.add('atom-text-editor', 'Python run: run-f5', run_f5), - atom.commands.add('atom-text-editor', 'Python run: run-f6', run_f6) - ); + + let config = { + 'pipeFile': atom.config.get('atom-python-run.pipeFile'), + 'pipePath': atom.config.get('atom-pyhton-run.pipePath'), + 'extensions': atom.config.get('atom-python-run.extensionFilter'), + 'log': atom.config.get('atom-python-run.log') + }; + + let disposables = [ + atom.commands.add('atom-text-editor', 'Python run: run-f5', runF5), + atom.commands.add('atom-text-editor', 'Python run: run-f6', runF6) + ]; + + function runF5() { + run(Object.assign(config, { + 'command': atom.config.get('atom-python-run.f5Command'), + 'pause': atom.config.get('atom-python-run.f5Pause') + })); + } + + function runF6() { + run(Object.assign(config, { + 'command': atom.config.get('atom-python-run.f6Command'), + 'pause': atom.config.get('atom-python-run.f6Pause') + })); + } + + function run(config) { + let editor = atom.workspace.getActiveTextEditor(); + let info = path.parse(editor.buffer.file.path); + let args = config.command.split(' '); + let shell = new terminal.Shell(__dirname + '/../cp/main.py'); + let spawn = new terminal.SpawnWrapper(shell.object); + + function format(string, object) { + return string.replace(/{.*?}/g, (element) => + object[element.substring(1, element.length - 1)] + ); + } + + editor.save(); + + args.forEach((item, index, arr) => { + arr[index] = format(item, { + 'file': editor.buffer.file.path, + 'dir': info.dir, + 'name': info.name, + 'ext': info.ext, + }); + }); + + if (config.extensions.length && !config.extensions.includes(info.ext)) { + return; + } + + /* + NOTE: 'cp' should always be on! + `cp` should ALWAYS default to pausing. when `cp` is set to NOT pause, + it should default to writing stdout and stderr to a file instead. + this will always force the argument tokens to be checked. + */ + if (!config.pause && !config.pipeFile) { + console.log("atom-python-run: Exception: 'cp' has been completely disabled!"); + console.log("Cowardly refusing to continue..."); + return; + } + + let tty = spawn.tty({ + 'pause': config.pause, + 'pipeFile': config.pipeFile, + 'pipePath': config.pipePath, + 'log': config.log, + 'args': args, + 'options': { + cwd: info.dir, + detached: true + } + }); + + tty.unref(); + } + + this.subscriptions.add(...disposables); }, deactivate() { this.subscriptions.dispose(); }, config: { - f5_command: { - title: 'Command of F5', + f5Command: { + title: 'F5 Command', description: '`{file}` = `{dir}/{name}{ext}`', type: 'string', - default: 'python {file}', + default: 'python {file}' }, - f6_command: { - title: 'Command of F6', + f6Command: { + title: 'F6 Command', description: 'Same as above', type: 'string', - default: 'python {file}', + default: 'python {file}' + }, + pipePath: { + title: 'Pipe to file', + description: 'Note: There is no default `{path}`; It is a dummy value', + type: 'string', + default: '{path}' }, - extension_filter: { + extensionFilter: { title: 'Extension filter', description: 'Leave it empty it accepts all files, or you can try `.py`', type: 'array', items: { type: 'string', }, - default: [], + default: [] }, - f5_cp: { - title: 'Use `cp` (F5)', - description: 'Show elapsed time and pause', + pipeFile: { + title: 'Pipe to File', + description: 'Pipe `cp`s output to a file', + type: 'boolean', + default: false + }, + f5Pause: { + title: 'Pause (F5)', + description: 'Show elapsed time and pause `cp`', type: 'boolean', - default: true, + default: true }, - f6_cp: { - title: 'Use `cp` (F6)', - description: 'Show elapsed time and pause', + f6Pause: { + title: 'Pause (F6)', + description: 'Show elapsed time and pause `cp`', type: 'boolean', - default: true, + default: true }, + log: { + title: 'Console Log', + description: 'Output log to `atom`s built-in console (`Note: Slow`)', + type: 'boolean', + default: true + } }, - subscriptions: null, + subscriptions: null }; - -function run_f5() { - run(atom.config.get('atom-python-run.f5_command'), - atom.config.get('atom-python-run.f5_cp')); -} - -function run_f6() { - run(atom.config.get('atom-python-run.f6_command'), - atom.config.get('atom-python-run.f6_cp')); -} - -function run(command, cp) { - let editor = atom.workspace.getActiveTextEditor(); - let file = editor.buffer.file; - let info = path.parse(file.path); - let extensions = atom.config.get('atom-python-run.extension_filter'); - let args = command.split(' '); - let shell = new terminal.Shell(__dirname + '/../cp/main.py'); - let spawn = new terminal.SpawnWrapper(shell.object); - - function invalid(extensions) { - if (extensions.length && !extensions.includes(info.ext)) { - return true; - } - return false; - } - - function parse(array) { - function format(string, object) { - return string.replace(/{.*?}/g, element => - object[element.substring(1, element.length - 1)] - ); - } - array.forEach((item, index, arr) => { - arr[index] = format(item, { - 'file': file.path, - 'dir': info.dir, - 'name': info.name, - 'ext': info.ext, - }); - }); - return array; - } - - editor.save(); - - if (invalid(extensions)) { - return; - } - - args = parse(args); - - /* - NOTE: 'cp' should always be on! - there should be an option to toggle whether `cp` pauses instead. - by default, `cp` should ALWAYS default to pausing. - when `cp` is set to NOT pause, it should default to writing stdout and - stderr to a file instead. - this will always force the argument tokens to be checked. - */ - let tty = spawn.tty({ - 'log': true, - 'cp': cp, - 'args': args, - 'options': { - cwd: info.dir, - detached: true - } - }); - - tty.unref(); -} diff --git a/lib/test.js b/lib/test.js index d5a6188..a341459 100644 --- a/lib/test.js +++ b/lib/test.js @@ -8,7 +8,7 @@ let object = { 'options': {}, 'args': [ 'python', - `${process.env.HOME}/Documents/Python/hello.py` + `${process.env.USERPROFILE}/Documents/Python/hello.py` ] } From c993b3d0f9960b55d7501cc9f1a256f58fe6f46d Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Mon, 25 Sep 2017 20:57:20 -0400 Subject: [PATCH 50/77] Fixed major bugs and added features. --- lib/atom-python-run.js | 73 ++++++++++++++++++++++++------------------ lib/terminal.js | 56 +++++++++++++++++++++----------- lib/test.js | 19 ----------- 3 files changed, 79 insertions(+), 69 deletions(-) delete mode 100644 lib/test.js diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index ef1030c..8d476a7 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -8,38 +8,36 @@ module.exports = { this.subscriptions = new CompositeDisposable(); - let config = { - 'pipeFile': atom.config.get('atom-python-run.pipeFile'), - 'pipePath': atom.config.get('atom-pyhton-run.pipePath'), - 'extensions': atom.config.get('atom-python-run.extensionFilter'), - 'log': atom.config.get('atom-python-run.log') - }; - - let disposables = [ - atom.commands.add('atom-text-editor', 'Python run: run-f5', runF5), - atom.commands.add('atom-text-editor', 'Python run: run-f6', runF6) - ]; - function runF5() { - run(Object.assign(config, { - 'command': atom.config.get('atom-python-run.f5Command'), - 'pause': atom.config.get('atom-python-run.f5Pause') - })); + run({ 'command': atom.config.get('atom-python-run.f5Command'), + 'pause': atom.config.get('atom-python-run.f5Pause'), + }); } function runF6() { - run(Object.assign(config, { + run({ 'command': atom.config.get('atom-python-run.f6Command'), - 'pause': atom.config.get('atom-python-run.f6Pause') - })); + 'pause': atom.config.get('atom-python-run.f6Pause'), + }); } + let disposables = [ + atom.commands.add('atom-text-editor', 'Python run: run-f5', runF5), + atom.commands.add('atom-text-editor', 'Python run: run-f6', runF6) + ]; + function run(config) { + let shell, spawn, tty; let editor = atom.workspace.getActiveTextEditor(); let info = path.parse(editor.buffer.file.path); let args = config.command.split(' '); - let shell = new terminal.Shell(__dirname + '/../cp/main.py'); - let spawn = new terminal.SpawnWrapper(shell.object); + + config = Object.assign(config, { + 'pipeFile': atom.config.get('atom-python-run.pipeFile'), + 'pipePath': atom.config.get('atom-python-run.pipePath'), + 'extensions': atom.config.get('atom-python-run.extensionFilter'), + 'log': atom.config.get('atom-python-run.log') + }); function format(string, object) { return string.replace(/{.*?}/g, (element) => @@ -62,19 +60,27 @@ module.exports = { return; } - /* - NOTE: 'cp' should always be on! - `cp` should ALWAYS default to pausing. when `cp` is set to NOT pause, - it should default to writing stdout and stderr to a file instead. - this will always force the argument tokens to be checked. - */ + // NOTE: 'cp' should always be on! if (!config.pause && !config.pipeFile) { - console.log("atom-python-run: Exception: 'cp' has been completely disabled!"); + console.log("atom-python-run: `pause` and `pipe` options have been disabled."); + console.log("Cowardly refusing to continue..."); + return; + } + if (config.pipeFile && ('' === config.pipePath || null == config.pipePath)) { + console.log("atom-python-run: `pipe` option has been enabled, but has no file to write to."); console.log("Cowardly refusing to continue..."); return; } - let tty = spawn.tty({ + if ('win32' === process.platform) { + shell = new terminal.Shell(__dirname + '\\..\\cp\\main.py'); + } else { + shell = new terminal.Shell(__dirname + '/../cp/main.py'); + } + + spawn = new terminal.SpawnWrapper(shell.object); + + tty = spawn.tty({ 'pause': config.pause, 'pipeFile': config.pipeFile, 'pipePath': config.pipePath, @@ -86,6 +92,11 @@ module.exports = { } }); + if (null == tty) { + console.log('atom-python-run: ExceptionError: `terminal` refused to spawn a `tty` instance'); + return; + } + tty.unref(); } @@ -109,9 +120,9 @@ module.exports = { }, pipePath: { title: 'Pipe to file', - description: 'Note: There is no default `{path}`; It is a dummy value', + description: 'The `destination` or `file` to write to', type: 'string', - default: '{path}' + default: '' }, extensionFilter: { title: 'Extension filter', diff --git a/lib/terminal.js b/lib/terminal.js index 961ba9a..aa5718c 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -223,11 +223,14 @@ class Log extends Base { ); } - params() { + data() { console.log( - `options: ${JSON.stringify(this.object.options, null, 4)}\n` + + `pause: ${this.object.pause}\n` + + `pipeFile: ${this.object.pipeFile}\n` + + `pipePath: ${this.object.pipePath}\n` + + `log: ${this.object.log}\n` + `args: ${this.object.args}\n` + - `cp: ${this.object.cp}` + `options: ${JSON.stringify(this.object.options, null, 4)}` ); } @@ -245,7 +248,7 @@ class Log extends Base { call() { this.metaData(); - this.params(); + this.data(); this.tty(); } } @@ -257,17 +260,33 @@ class SpawnWrapper extends Base { this.set(base, ...source); } + spawnArgs(model) { + if (model.pause && !model.pipeFile) { + return [this.object.call, this.object.script, + '--pause', ...model.args + ]; + } + if (!model.pause && model.pipeFile) { + return [this.object.call, this.object.script, + '--file', model.pipePath, ...model.args + ]; + } + if (model.pause && model.pipeFile) { + return [this.object.call, this.object.script, + '-p', '--file', model.pipePath, ...model.args + ]; + } + return null; // always refuse to pass foreign arguments + } + spawnWin32(model) { - function cp(object, ...args) { - if (model.cp) { - return [...object.option, object.call, object.script, ...args]; - } else { - return [...object.option, ...args]; - } + let args = this.spawnArgs(model); + if (null == args) { + return null; } return spawn( this.object.shell, - [...cp(this.object, ...model.args)], + [...this.object.option, ...args], model.options ) } @@ -275,11 +294,13 @@ class SpawnWrapper extends Base { spawnDarwin(model) { // darwin should never be modified or configured in any way. doing // so risks breaking the required tool chain. + let args = this.spawnArgs(model); + if (null == args) { + return null; + } return spawn( this.object.shell, - [ ...this.object.option, - this.object.command(...model.args) - ], + [...this.object.option, this.object.command(...args)], model.options ); } @@ -320,11 +341,8 @@ class SpawnWrapper extends Base { if (model.log) { new Log({ 'tty': tty, - 'args': model.args, - 'options': model.options, - 'cp': model.cp, - 'meta': this.object - }).call(); + 'meta': this.object, + }, model).call(); } return tty; } diff --git a/lib/test.js b/lib/test.js deleted file mode 100644 index a341459..0000000 --- a/lib/test.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -const terminal = require('./terminal'); - -let object = { - 'log': true, - 'cp': true, - 'options': {}, - 'args': [ - 'python', - `${process.env.USERPROFILE}/Documents/Python/hello.py` - ] -} - -let shell = new terminal.Shell(); - -let spawn = new terminal.SpawnWrapper(shell.object); - -spawn.tty(object); From 56e2d545aaf0563951fc5b857b0c8118b5536981 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Wed, 27 Sep 2017 21:42:20 +0800 Subject: [PATCH 51/77] Prepare 0.9.0 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 743f4dd..4036fe0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.8.1", + "version": "0.9.0", "description": "Run a python source file.", "keywords": [ "python" From c08666227afa4ddfcd568ea354267e01358dab17 Mon Sep 17 00:00:00 2001 From: jargon Date: Sat, 30 Sep 2017 19:43:56 -0400 Subject: [PATCH 52/77] patched the defaultLogPath platform comparision method --- cp/cp/parse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cp/cp/parse.py b/cp/cp/parse.py index a48714c..b23e4a3 100644 --- a/cp/cp/parse.py +++ b/cp/cp/parse.py @@ -56,7 +56,7 @@ def hasLogPath(self): return isdir(dirname(self._logPath)) def defaultLogPath(self): - if 'win' in platform: + if 'win32' == platform: return "{}\\.atom\\packages\\atom-python-run\\cp.log".format(environ['USERPROFILE']) return "{}/.atom/packages/atom-python-run/cp.log".format(environ['HOME']) From d93689b58beb8161a6d9281eb44e71822983db9c Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Mon, 2 Oct 2017 17:34:09 -0400 Subject: [PATCH 53/77] Updated comments... --- cp/cp/fnp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cp/cp/fnp.py b/cp/cp/fnp.py index 5711d79..1ef1549 100644 --- a/cp/cp/fnp.py +++ b/cp/cp/fnp.py @@ -13,7 +13,7 @@ # # I came to this conclusion after some time, consideration, and extensive testing. # -# Typically, fragile code is bad code; in this case it couldn't be helped. +# Fragile code is always bad code; sadly in this case I couldn't helped it. # Any desired changes or fixes could impact other portions of the code. # The major trade off seemed to be between "code complexity" and "execution speed". # This module runs more "accurately" and "effeciently" than using a module fitted for regex. From 81c1c2c21a41e8b423da661fdc553df9b20db5f7 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Tue, 3 Oct 2017 17:33:46 +0800 Subject: [PATCH 54/77] Prepare 0.9.1 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4036fe0..4523ad4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.9.0", + "version": "0.9.1", "description": "Run a python source file.", "keywords": [ "python" From 6c6d23da6e799f723e762c57b65826d77081b477 Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Tue, 3 Oct 2017 21:52:42 -0400 Subject: [PATCH 55/77] darwin patch - fixed executing script in terminal app --- cp/cp/parse.py | 6 +++--- cp/cp/utils.py | 9 +++++++++ cp/main.py | 3 +++ lib/terminal.js | 4 ++-- lib/test.js | 23 +++++++++++++++++++++++ 5 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 cp/cp/utils.py create mode 100644 lib/test.js diff --git a/cp/cp/parse.py b/cp/cp/parse.py index b23e4a3..abfaba3 100644 --- a/cp/cp/parse.py +++ b/cp/cp/parse.py @@ -129,11 +129,11 @@ def exitCode(self): class Parser(Command, Call, Clock, Logpath): def pause(self): - if 'win' in platform: + if 'win32' == platform: system('pause') - elif 'darwin' in platform: + elif 'darwin' == platform: system('echo "Close this window to continue..."') - elif 'linux' in platform: + elif 'linux' == platform[:5]: system("printf 'Press [ENTER] to continue...'; read _;") else: logging.info('Unknown OS Type: %s', platform) diff --git a/cp/cp/utils.py b/cp/cp/utils.py new file mode 100644 index 0000000..b29a1f9 --- /dev/null +++ b/cp/cp/utils.py @@ -0,0 +1,9 @@ + +from sys import platform +from os import system + +def clear(): + if 'win32' == platform: + system('cls') + else: + system('clear') diff --git a/cp/main.py b/cp/main.py index ac3fe89..05cacdd 100644 --- a/cp/main.py +++ b/cp/main.py @@ -30,6 +30,7 @@ from sys import argv, platform from logging import basicConfig, DEBUG, info from cp.parse import Parser +from cp.utils import clear version_info = "v0.2.0" @@ -61,6 +62,8 @@ parser.setCommand() + clear() + if namespace['file']: parser.pipeCall('a') else: diff --git a/lib/terminal.js b/lib/terminal.js index aa5718c..a78e8d8 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -146,7 +146,7 @@ class MetaShell extends Base { let object = this.get(); this.set(object, { 'command': function (args) { - let path = `${object.call} ${object.script}`; + let path = ''; for (let token of args) { path += ` ${token}`; } @@ -300,7 +300,7 @@ class SpawnWrapper extends Base { } return spawn( this.object.shell, - [...this.object.option, this.object.command(...args)], + [...this.object.option, this.object.command(args)], model.options ); } diff --git a/lib/test.js b/lib/test.js new file mode 100644 index 0000000..72088f4 --- /dev/null +++ b/lib/test.js @@ -0,0 +1,23 @@ +const terminal = require('./terminal'); + +let shell, spawn; + +shell = new terminal.Shell() + +// console.log(shell.object); + +spawn = new terminal.SpawnWrapper(shell.object); + +// console.log(spawn.object); + +tty = spawn.tty({ + 'pause': true, + 'pipeFile': false, + 'pipePath': '', + 'log': true, + 'args': ['python', `${process.env.USERPROFILE}\\Dropbox\\Source\\Python\\hello.py`], + // 'args': ['python', `${process.env.HOME}/Dropbox/Source/Python/hello.py`], + 'options': new Object() +}); + +tty.unref(); From ae30c9186e98f24d97b13de1ee11a335bcc651de Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Wed, 4 Oct 2017 23:30:54 -0400 Subject: [PATCH 56/77] cp options patch - allow opting out of pausing --- lib/atom-python-run.js | 6 ------ lib/terminal.js | 12 ++---------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index 8d476a7..e316797 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -60,12 +60,6 @@ module.exports = { return; } - // NOTE: 'cp' should always be on! - if (!config.pause && !config.pipeFile) { - console.log("atom-python-run: `pause` and `pipe` options have been disabled."); - console.log("Cowardly refusing to continue..."); - return; - } if (config.pipeFile && ('' === config.pipePath || null == config.pipePath)) { console.log("atom-python-run: `pipe` option has been enabled, but has no file to write to."); console.log("Cowardly refusing to continue..."); diff --git a/lib/terminal.js b/lib/terminal.js index a78e8d8..c1d06a8 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -273,17 +273,14 @@ class SpawnWrapper extends Base { } if (model.pause && model.pipeFile) { return [this.object.call, this.object.script, - '-p', '--file', model.pipePath, ...model.args + '-p', '-f', model.pipePath, ...model.args ]; } - return null; // always refuse to pass foreign arguments + return [this.object.call, this.object.script, ...model.args]; } spawnWin32(model) { let args = this.spawnArgs(model); - if (null == args) { - return null; - } return spawn( this.object.shell, [...this.object.option, ...args], @@ -292,12 +289,7 @@ class SpawnWrapper extends Base { } spawnDarwin(model) { - // darwin should never be modified or configured in any way. doing - // so risks breaking the required tool chain. let args = this.spawnArgs(model); - if (null == args) { - return null; - } return spawn( this.object.shell, [...this.object.option, this.object.command(args)], From 8dc9348efe8b82e640a804f7757999c1d4babf8c Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Thu, 5 Oct 2017 02:17:08 -0400 Subject: [PATCH 57/77] settings patch - fixed and added config settings --- lib/atom-python-run.js | 83 ++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index e316797..efa31ee 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -32,11 +32,13 @@ module.exports = { let info = path.parse(editor.buffer.file.path); let args = config.command.split(' '); + // load configuration settings in to 'config' object config = Object.assign(config, { + 'terminal': atom.config.get('atom-python-run.terminal'), 'pipeFile': atom.config.get('atom-python-run.pipeFile'), 'pipePath': atom.config.get('atom-python-run.pipePath'), 'extensions': atom.config.get('atom-python-run.extensionFilter'), - 'log': atom.config.get('atom-python-run.log') + 'consoleLog': atom.config.get('atom-python-run.consoleLog') }); function format(string, object) { @@ -74,11 +76,27 @@ module.exports = { spawn = new terminal.SpawnWrapper(shell.object); + // set up a user defined 'shell' + if (2 <= config.terminal.length) { + // requires a 'shell' name and at least one 'option' + if (config.terminal[0] != null && config.terminal[1] != null) { + spawn.object.shell = config.terminal[0]; + // more than one option may be required depending on the system + spawn.object.option = []; + for (let option of config.terminal) { + if (option === config.terminal[0]) { + continue; + } + spawn.object.option.push(option); + } + } + } + tty = spawn.tty({ 'pause': config.pause, 'pipeFile': config.pipeFile, 'pipePath': config.pipePath, - 'log': config.log, + 'log': config.consoleLog, 'args': args, 'options': { cwd: info.dir, @@ -100,6 +118,21 @@ module.exports = { this.subscriptions.dispose(); }, config: { + consoleLog: { + title: 'Console Log', + description: 'Output log to `atom`s built-in console (`Slow`)', + type: 'boolean', + default: true + }, + extensionFilter: { + title: 'Extension filter', + description: 'Leave it empty it accepts all files, or you can try `.py`', + type: 'array', + items: { + type: 'string', + }, + default: [] + }, f5Command: { title: 'F5 Command', description: '`{file}` = `{dir}/{name}{ext}`', @@ -112,27 +145,6 @@ module.exports = { type: 'string', default: 'python {file}' }, - pipePath: { - title: 'Pipe to file', - description: 'The `destination` or `file` to write to', - type: 'string', - default: '' - }, - extensionFilter: { - title: 'Extension filter', - description: 'Leave it empty it accepts all files, or you can try `.py`', - type: 'array', - items: { - type: 'string', - }, - default: [] - }, - pipeFile: { - title: 'Pipe to File', - description: 'Pipe `cp`s output to a file', - type: 'boolean', - default: false - }, f5Pause: { title: 'Pause (F5)', description: 'Show elapsed time and pause `cp`', @@ -145,11 +157,26 @@ module.exports = { type: 'boolean', default: true }, - log: { - title: 'Console Log', - description: 'Output log to `atom`s built-in console (`Note: Slow`)', - type: 'boolean', - default: true + pipeFile: { + title: 'Pipe to File', + description: 'Pipe `cp`s output to a file', + type: 'boolean', + default: false + }, + pipePath: { + title: 'Pipe to file', + description: 'The `destination` or `file` to write to', + type: 'string', + default: '' + }, + terminal: { + title: 'Terminal', + description: 'Leave it empty to use default `shell`, `option`; or you can try `terminator`, `-x`', + type: 'array', + items: { + type: 'string', + }, + default: [] } }, subscriptions: null From 158ceeacaf686da422d0d3f23478342066484615 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Thu, 5 Oct 2017 18:45:06 +0800 Subject: [PATCH 58/77] Prepare 0.9.2 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4523ad4..5a54dbd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.9.1", + "version": "0.9.2", "description": "Run a python source file.", "keywords": [ "python" From 9042036ee36aa45ff1566f11c5f14d0009a47cbb Mon Sep 17 00:00:00 2001 From: Gabryel Monteiro Date: Mon, 9 Oct 2017 12:55:33 -0300 Subject: [PATCH 59/77] Warning user about possibility of terminal choice --- lib/terminal.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/terminal.js b/lib/terminal.js index c1d06a8..35696cb 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -330,6 +330,15 @@ class SpawnWrapper extends Base { tty(model) { model.options = this.optionWrapper(model.options); let tty = this.spawnWrapper(model); + let shellName = this.object.shell; + + tty.on('error', function (error) { + atom.notifications.addError('There was a problem opening \'' + + shellName + '\'. If your computer doesn\'t have \'' + shellName + + '\' installed, you can set your own terminal on the package\'s ' + + 'Settings.') + }); + if (model.log) { new Log({ 'tty': tty, From f7d901b6e221624ed6dabbe57030f31c88c89c03 Mon Sep 17 00:00:00 2001 From: jargon Date: Tue, 10 Oct 2017 17:20:14 -0400 Subject: [PATCH 60/77] Cosmetic changes... --- cp/cp/utils.py | 1 + lib/test.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cp/cp/utils.py b/cp/cp/utils.py index b29a1f9..098a19a 100644 --- a/cp/cp/utils.py +++ b/cp/cp/utils.py @@ -2,6 +2,7 @@ from sys import platform from os import system + def clear(): if 'win32' == platform: system('cls') diff --git a/lib/test.js b/lib/test.js index 72088f4..1366d7c 100644 --- a/lib/test.js +++ b/lib/test.js @@ -15,8 +15,8 @@ tty = spawn.tty({ 'pipeFile': false, 'pipePath': '', 'log': true, - 'args': ['python', `${process.env.USERPROFILE}\\Dropbox\\Source\\Python\\hello.py`], - // 'args': ['python', `${process.env.HOME}/Dropbox/Source/Python/hello.py`], + // 'args': ['python', `${process.env.USERPROFILE}\\Dropbox\\Source\\Python\\hello.py`], + 'args': ['python', `${process.env.HOME}/Documents/Python/hello.py`], 'options': new Object() }); From 4e4ce1f1699c505944edce97c57d1ddae57423d4 Mon Sep 17 00:00:00 2001 From: jargon Date: Tue, 10 Oct 2017 19:28:37 -0400 Subject: [PATCH 61/77] Notify users on failure: Thanks @Megamiun for the code and suggestions. --- lib/atom-python-run.js | 20 ++++++++++++++------ lib/terminal.js | 8 -------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index efa31ee..b06f523 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -58,13 +58,18 @@ module.exports = { }); }); + if (2 > args.length) { + atom.notifications.addError(`atom-python-run: \`FnKeyError\`: Invalid argument length for \`Fn-Key Command\`: requires \`repl [options] {file}\` formatting. Got \`${config.command}\` instead.`); + return; + } + if (config.extensions.length && !config.extensions.includes(info.ext)) { + atom.notifications.addError(`atom-python-run: \`ExtensionsError\`: \`extensions\` option has been enabled, but only \`${config.extensions}\` are allowed. Cowardly refusing to continue...`); return; } if (config.pipeFile && ('' === config.pipePath || null == config.pipePath)) { - console.log("atom-python-run: `pipe` option has been enabled, but has no file to write to."); - console.log("Cowardly refusing to continue..."); + atom.notifications.addError(`atom-python-run: \`PipeError\`: \`pipe\` option has been enabled, but has no \`file\` to write to. Cowardly refusing to continue...`); return; } @@ -104,12 +109,15 @@ module.exports = { } }); - if (null == tty) { - console.log('atom-python-run: ExceptionError: `terminal` refused to spawn a `tty` instance'); + try { + tty.on('error', function (error) { + atom.notifications.addError(`atom-python-run: \`TerminalError\`: There was a problem opening \`${spawn.object.shell}\`. If your computer doesn't have \`${spawn.object.shell}\` installed, you can set your own terminal on the package's Settings.`); + }); + tty.unref(); + } catch(e) { + atom.notifications.addError(`atom-python-run: \`ExceptionError\`: \`terminal\` refused to spawn a \`tty\` instance: \`${e}\``); return; } - - tty.unref(); } this.subscriptions.add(...disposables); diff --git a/lib/terminal.js b/lib/terminal.js index 35696cb..6e4b797 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -330,14 +330,6 @@ class SpawnWrapper extends Base { tty(model) { model.options = this.optionWrapper(model.options); let tty = this.spawnWrapper(model); - let shellName = this.object.shell; - - tty.on('error', function (error) { - atom.notifications.addError('There was a problem opening \'' - + shellName + '\'. If your computer doesn\'t have \'' + shellName - + '\' installed, you can set your own terminal on the package\'s ' + - 'Settings.') - }); if (model.log) { new Log({ From a47440db8e4122368dd7ef5de5ca24d4008d3e71 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Wed, 11 Oct 2017 18:27:41 +0800 Subject: [PATCH 62/77] Prepare 0.9.3 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a54dbd..b000ccd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.9.2", + "version": "0.9.3", "description": "Run a python source file.", "keywords": [ "python" From 1fa8e50592dbfd101d5c420f0dbbb521cf37050d Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Wed, 15 Nov 2017 22:06:26 -0500 Subject: [PATCH 63/77] patch to allow arbitrary arguments. --- cp/cp/fnp.py | 339 -------------------------------------------- cp/cp/lexer.py | 367 ------------------------------------------------ cp/cp/parse.py | 41 +++--- cp/main.py | 7 +- lib/terminal.js | 8 +- 5 files changed, 32 insertions(+), 730 deletions(-) delete mode 100644 cp/cp/fnp.py delete mode 100644 cp/cp/lexer.py diff --git a/cp/cp/fnp.py b/cp/cp/fnp.py deleted file mode 100644 index 1ef1549..0000000 --- a/cp/cp/fnp.py +++ /dev/null @@ -1,339 +0,0 @@ -"""fnp - discern whether the given string is a valid file name and/or path.""" -# -# ###### -# NOTE # -######## -# Some people, when confronted with a problem, think -# "I know, I'll use regular expressions." Now they have two problems. -# -# I find nothing wrong with using regular expressions when appropriate. In the end, -# I chose "structured code" over "regex" for "precision and speed". -# -# This module seems to be the "better" choice overall for this single use case. -# -# I came to this conclusion after some time, consideration, and extensive testing. -# -# Fragile code is always bad code; sadly in this case I couldn't helped it. -# Any desired changes or fixes could impact other portions of the code. -# The major trade off seemed to be between "code complexity" and "execution speed". -# This module runs more "accurately" and "effeciently" than using a module fitted for regex. -# -# I needed a single use function for determining whether or not a given string -# could pass as a "valid" file name and/or a "valid" path to a file name. -# -# This module checks a given string and determines whether or not it is a valid file path. -# It does not (nor should it) determine a files creditablity or accessability. -# -# NOTE: The file need not exist nor be created. It should return True on any -# path considered "valid" as long as it is not pointing to a directory. -# -# ################ -# Absolute Paths # -# ################ -# : ]- Current Directory --| -# |-- Windows Only - requires alpha prefix -# :\ ]- Root Directory --| -# -# / ]- Root Directory |-- *nix Only - required prefix for absolute path -# -# ################ -# Relative Paths # -# ################ -# Relative paths are universal (for the most part) -# -# Windows allows a '/' or a '\' as long as a single form is used -# Unix requires a '/' and uses the '\' character as an escape character -# -# . .. ./ .\ ../ ..\ ]-- all valid relative path prefixes -# -# ################# -# Path Seperators # -# ################# -# whether or not any valid prefix is correct is system dependent. -# this implementation is system dependent and not all forms are considered valid. -# -# i.e.: the following command line is valid on a win32 system -# $ cd C:/Users/Username/Documents -# -# NOTE: the this will fail when this module is used because the seperator value is defined -# by the os.path.sep variable and then used to validate the given path like patterns. -# -# this module would only consider C:\Users\Username\Documents to be valid -# -from __future__ import print_function -from sys import platform -from os.path import sep, isdir -from string import ascii_letters, digits - -__all__ = ('FileNamePattern', 'filenames', 'winpaths', 'nixpaths') - - -class Base(object): - def __init__(self, charset=None): - self._charset = charset - self._sep = sep - self._legalPathSet = [":", ":\\", "/", "./", ".\\", "../", "..\\"] - self._legalCharSet = ascii_letters + digits + '_.' - - def getCharSet(self): - return self._charset - - def setCharSet(self, charset): - self._charset = charset - - -class Grammar(object): - # requires exception since value is unknown - def hasLegalPath(self, start, stop=None): - try: - if stop is None: - return self._charset[start] in self._legalPathSet - return self._charset[start:stop] in self._legalPathSet - except IndexError: - return False - - # requires exception since value is unknown - def hasLegalChar(self, position): - try: - return self._charset[position] in self._legalCharSet - except IndexError: - return False - - def nextCharIsLegal(self, position): - return self.hasLegalChar(position + 1) - - # NOTE: will always return an empty string on failure - def endsWithPathSep(self): # this block is here for testing purposes - return self._charset[-1:] == self._sep - - # requires exception since value is unknown - def startsWithChar(self): - try: - return self._charset[0].isalpha() - except IndexError: - return False - - def startsWithLegalChar(self): - return self.hasLegalChar(0) - - def startsWithPathSep(self): - return self.hasLegalPath(0) and self._charset[0] == self._sep - - def startsWithCurrentDir(self): - return self.hasLegalPath(0, 2) and self._charset[0] == '.' - - def startsWithParentDir(self): - return self.hasLegalPath(0, 3) and self._charset[0] == '.' - - def startsWithWinCurrentPath(self): - return self.startsWithChar() and self.hasLegalPath(1) \ - and self.hasLegalChar(2) - - def startsWithWinDrivePath(self): - return self.startsWithChar() and self.hasLegalPath(1, 3) - - -class Scanner(object): - def pathIsRelative(self): - return self.startsWithCurrentDir() or self.startsWithParentDir() - - def winAbsolutePath(self): - return self.startsWithWinCurrentPath() or self.startsWithWinDrivePath() - - def nixAbsolutePath(self): - return '/' == self._charset[0] - - def pathIsAbsolute(self): - return self.winAbsolutePath() or self.nixAbsolutePath() - - def pathIsLegal(self): - for pos, char in enumerate(self._charset): - if (char == self._sep or self.startsWithWinCurrentPath()) and self.nextCharIsLegal(pos): - return True - return False - - def pathIsValid(self): - if self.pathIsRelative() or self.pathIsAbsolute(): - return self.pathIsLegal() - return self.startsWithLegalChar() and self.nextCharIsLegal(0) - - -class FileNamePattern(Base, Grammar, Scanner): - def testHasFileName(self): - if self.endsWithPathSep(): - return False - return self.pathIsValid() - - def testDisplayPath(self, label, path): - print("{}:".format(label), "{}:".format(self.testHasFileName()), path) - - def hasFileName(self): - if isdir(self._charset): - return False - return self.pathIsValid() - - def displayPath(self, label, path): - print("{}:".format(label), "{}:".format(self.hasFileName()), path) - - -def main(): - fnp = FileNamePattern() - if platform == 'win32': - pathlist = filenames + winpaths - else: - pathlist = filenames + nixpaths - print("Label: Valid file name: File path") - for p in pathlist: - fnp.setCharSet(p) - fnp.testDisplayPath('Path', p) - - -filenames = [ - "filename", - ".filename", - "_filename", - "file name", - ".file name", - "_file name", - "filename.ext", - ".filename.ext", - "_filename.ext", - "file name.ext", - ".file name.ext", - "_file name.ext" -] - -winpaths = [ - "\\", - "\\.", - "\\_", - "\\home", - "\\home\\", - "\\home\\username", - "\\home\\username\\some dir", - "\\home\\username\\some dir\\", - "\\home\\username\\some dir\\path", - "home\\username\\some dir\\path", - "home\\username\\some dir\\path/", - ".home\\username\\some dir\\path", - ".home\\username\\some dir\\path\\", - "..home\\username\\some dir\\path", - "..home\\username\\some dir\\path\\", - "\\home\\username\\some dir\\path\\.", - "\\home\\username\\some dir\\path\\_", - "\\home\\username\\some dir\\path\\.filename", - "\\home\\username\\some dir\\path\\_filename", - "\\home\\username\\some dir\\path\\filename", - ".", - ".\\", - "..\\", - ".\\.", - "..\\.", - ".\\_", - "..\\_", - ".\\.filename", - "..\\.filename.ext", - ".\\_filename", - "..\\_filename.ext", - ".\\filename", - "..\\filename.ext", - ".\\Users", - "..\\Users\\Username", - ".\\Users\\Username\\", - ".\\Users\\Username\\Some file", - "..\\Users\\Username\\Some file\\", - ".\\Users\\Username\\Some file\\path", - "..\\Users\\Username\\Some file\\.path", - "...\\Users\\Username\\Some file\\_path", - "...\\Users\\Username\\Some file\\path\\filename.ext", - "..\\Users\\Username\\Some file\\.path\\filename", - ".\\Users\\Username\\Some file\\_path\\filename.ext", - "C", - "C:", - "C:", - "C:.", - "C:_", - "C:+", - "C:Users", - "C:Users\\Username", - "C:Users\\Username\\", - "C:Users\\Username\\Some file", - "C:Users\\Username\\Some file\\", - "C:Users\\Username\\Some file\\path", - "C:Users\\Username\\Some file\\.path", - "C:Users\\Username\\Some file\\_path", - "C:Users\\Username\\Some file\\path\\filename.ext", - "C:Users\\Username\\Some file\\.path\\filename", - "C:Users\\Username\\Some file\\_path\\filename.ext", - "C", - "C:", - "C:\\", - "C:\\.", - "C:\\_", - "C:\\+", - "C:\\Users", - "C:\\Users\\Username", - "C:\\Users\\Username\\", - "C:\\Users\\Username\\Some file", - "C:\\Users\\Username\\Some file\\", - "C:\\Users\\Username\\Some file\\path", - "C:\\Users\\Username\\Some file\\.path", - "C:\\Users\\Username\\Some file\\_path", - "C:\\Users\\Username\\Some file\\path\\filename.ext", - "C:\\Users\\Username\\Some file\\.path\\filename", - "C:\\Users\\Username\\Some file\\_path\\filename.ext", -] - -nixpaths = [ - ".", - "./", - "../", - "./.", - "../.", - "./_", - "../_", - "./.filename", - "../.filename.ext", - "./_filename", - "../_filename.ext", - "./filename", - "../filename.ext", - "./somedir/.filename", - "../somedir/.filename.ext", - "./somedir/_filename", - "../some/dir/path and name/_filename.ext", - "./some/dir/path and name/filename", - "../some/dir/path and name/filename.ext", - "/", - "/.", - "/_", - "/.filename", - "/.filename.ext", - "/_filename", - "/_filename.ext", - "/home", - "/home/", - "/home/username", - "/home/username/some dir", - "/home/username/some dir/", - "/home/username/some dir/path", - "home/username/some dir/path", - "home/username/some dir/path/", - ".home/username/some dir/path", - ".home/username/some dir/path/", - "..home/username/some dir/path", - "..home/username/some dir/path/", - "/home/username/some dir/path/.", - "/home/username/some dir/path/_", - "/home/username/some dir/path/.filename", - "/home/username/some dir/path/.filenameext", - "/home/username/some dir/path/_filename", - "/home/username/some dir/path/_filename.ext", - "/home/username/some dir/path/filename", - "/home/username/some dir/path/file name", - "/home/username/some dir/path/filename.ext", - "/home/username/some dir/path/file name.ext" -] - -if __name__ == '__main__': - main() diff --git a/cp/cp/lexer.py b/cp/cp/lexer.py deleted file mode 100644 index d8de835..0000000 --- a/cp/cp/lexer.py +++ /dev/null @@ -1,367 +0,0 @@ -"""lexer - structures and tests the argv token set as an AST for parsing.""" -# NOTE: use the main.py file instead if you want to execute scripts. -# -############ -# lexer.py # -############ -# while lexer.py is executable, it executes as a dummy file and doesn't actually -# execute anything directly. it simply demonstrates how lexer.py handles the -# "argv" variable. -# -# "argv" is a list and join could be used in conjunction to create a concatenated -# string. -# -# this string could be passed directly to command. it is up to the end user to -# make sure the command executes as intended. -# -############# -# Execution # -############# -# lexer.py is mainly designed to be an extremely lightweight check. -# -# it's rules restrict what can be passed to it while remaining flexible enough -# to be able achieve arbitrary execution. -# -# in example: -# -# this will always fail... and should always fail. -# -> "python" is the interpreter to be executed by lexer.py -# -> "-c" is the option to be passed to python (not lexer.py) -# -> "string" is the command to be executed by python (not lexer.py) -# -# the point of the file check is to determine if the file actually exists; not -# to see if it is executable. we also have no intention of writing to it. -# obviously, a string is executable, but we want to execute a file; not a string. -# -# $ python lexer.py python -c 'print("hello, world!")' -# Error: Expected optional option argument: got "'print(hello, world!)'" instead -# -# $ python -c "print('Hello, World!')" -# bash: !': event not found -# -# $ python -c 'print("hello, world!")' -# hello, world -# -############# -# Expansion # -############# -# NOTE: the shell should handle user and variable expansion it self. user, -# variable, and glob expansion should never occur. -# -# the final argument should always be a source file that can be executed by the -# given interpreter. -# -# $ python lexer.py node ~/documents/js/hello.js -# ...dictionary output here... -# -########### -# Options # -########### -# NOTE: lexer.py will handle its own options as long as they are placed before -# the interpreter argument. -# -# any arguments inbetween the interpreter and the filename are considered -# options to be passed to the interpreter itself. -# -# lexer.py also handles redirection. for example, if you want to pipe stdout and -# stderr to a file, you can do this -# -# $ python lexer.py -f out.log node ~/documents/js/hello.js -# ...dictionary output here... -# -# for more usage information, do -# -# $ python lexer.py -h -# ...help output here... -# -# an example on how to use the Lexer object is demonstrated in a function named -# "main()" at the end of the file. -# -from __future__ import print_function -from __future__ import absolute_import -from sys import argv -import os.path -import logging -from .fnp import FileNamePattern - - -__all__ = ("Lexer", "Scanner") - - -class BarePhraseStructure(object): - pass - - -class Namespace(object): - def __init__(self, args, progname=None): - self._opts = [ - '-h', '--help', - '-p', '--pause', - '-f', '--file' - ] - - self._optArgs = [ - '-f', '--file' - ] - - self._index = int() - self._error = str() - self._ast = args[1:] - - self._namespace = BarePhraseStructure() - self._namespace.name = progname or args[0] - self._namespace.pause = bool() - self._namespace.file = bool() - self._namespace.path = str() - self._namespace.repl = str() - self._namespace.options = list() - self._namespace.script = str() - self._namespace.last = args[-1:][0] - - try: - self.arg = args[1:][0] - except IndexError: - self.usage() - exit(1) - - self.logAst() - self.logToken() - - # Namespace - def getNamespace(self): - return vars(self._namespace) - - def printNamespace(self): - for key, value in self.getNamespace().items(): - print('{}: {}'.format(key, value)) - - -class Log(Namespace): - def logNamespace(self): - for key, value in self.getNamespace().items(): - logging.debug('namespace: [key] "%s" [value] "%s"', key, value) - - def logToken(self): - logging.debug('Index: "%s" Token: "%s"', self._index, self.arg) - - def logError(self): - logging.error(self._error) - - def logAst(self): - logging.debug('_ast: "%s"', self._ast) - - -class Error(Namespace): - def setError(self, taxonomy, error, message=None): - if message: - self._error = '{}: {}: {}'.format(taxonomy, error, message) - else: - self._error = '{}: {}'.format(taxonomy, error) - - def getError(self): - return self._error - - def error(self, taxonomy, error, message=None): - self.setError(taxonomy, error, message) - self.logError() - print(self._error) - exit(1) - - -class Token(Namespace): - def shift(self): - self._index += 1 - - def setToken(self, error=None, message=None): - try: - self.arg = self._ast[self._index] - except IndexError: - self.error('Error', error, message) - - def shiftToken(self, error=None, message=None): - self.shift() - self.setToken(error, message) - self.logToken() - - def getToken(self): - return self.arg - - def printToken(self): - print(self.arg) - - -class Help(Namespace): - def usage(self): - print('Usage: {} [-h] [-p] [-f [filename]] repl args [args...]'.format(self._namespace.name)) - - def summary(self): - print('{} - handles arbitrary arguments to be executed.\n'.format(self._namespace.name)) - print('Usage\n-----\n\t{} [-h] [-p] [-f [filename]] repl args [args...]'.format(self._namespace.name)) - print( - "\n" - "Options\n" - "-------\n" - "\t-h\t--help\t\tprints this help text\n" - "\t-f\t--file\t\tpipes 'stdout' and 'stderr' to the given file\n" - "\t-p\t--pause\t\tprompt client for 'stdin'\n" - "\n" - "Parameters\n" - "----------\n" - "\trepl\t\tthe repl used to execute 'script'\n" - "\tscript\t\t\tthe file to be executed by 'repl'\n" - "\n" - ) - print('Example: {} -p -f output.log python -i source.py'.format(self._namespace.name)) - - -class Grammar(Log, Error, Token, Help): - def hasOption(self): - return '-' == self.arg[0] - - def hasValidOption(self): - return self.arg in self._opts - - def hasOptionArgument(self): - return self.arg in self._optArgs - - def hasOptionEnd(self): - return 1 == len(self.arg) - - def endsOptionScan(self): - return self.hasOptionEnd() if self.hasOption() else not self.hasOption() - - def hasHelpOption(self): - return '-h' == self.arg or '--help' == self.arg - - def hasPauseOption(self): - return '-p' == self.arg or '--pause' == self.arg - - def hasFileOption(self): - return '-f' == self.arg or '--file' == self.arg - - def hasScript(self): # TOCTOU - needs a better way - return os.path.isfile(self.arg) and os.path.isfile(self._namespace.last) - - def hasRepl(self): - return not self.hasOption() and not self.hasScript() and self.arg - - def hasValidFileName(self): - fnp = FileNamePattern(self.arg) - return fnp.hasFileName() - - def _setHelpOption(self): - if self.hasHelpOption(): - self.summary() - exit(0) - - def _setPauseOption(self): - if self.hasPauseOption(): - self._namespace.pause = True - - def _setFileOption(self): - if self.hasFileOption(): - self._namespace.file = True - - def _setFilePath(self): - if self.hasOptionArgument(): - self.shiftToken('Missing required file path for option "{}"'.format(self.arg)) - self._exitOnInvalidFilename() - self._namespace.path = self.arg - - def _setScannedOption(self): - if self.hasValidOption(): - self._setHelpOption() - self._setPauseOption() - self._setFileOption() - self._setFilePath() - self.shiftToken( - 'Expected required repl argument', - 'got "{}" instead'.format(self.arg) - ) - - def _exitOnInvalidFilename(self): - if not self.hasValidFileName(): - self.error( - 'Error', - 'Expected required option argument "filename"', - 'got "{}" instead'.format(self.arg) - ) - - def _exitOnInvalidOption(self): - if not self.hasValidOption(): - self.error( - 'Error', - 'Expected optional argument "option"', - 'got "{}" instead'.format(self.arg) - ) - - def _exitOnInvalidRepl(self): - if not self.hasRepl(): - self.error( - 'Error', - 'Expected required argument "repl"', - 'got "{}" instead'.format(self.arg) - ) - - def _exitOnMissingScript(self): - if not self.hasScript(): - self.error( - 'Error', - 'Expected required argument "script"', - 'got "{}" instead'.format(self.arg) - ) - - -class Scanner(Grammar): - def scanOptions(self): - while not self.endsOptionScan(): - self._exitOnInvalidOption() - self._setScannedOption() - - def scanRepl(self): - self._exitOnInvalidRepl() - self._namespace.repl = self.arg - self.shiftToken( - 'Expected required script argument', - 'got "{}" instead'.format(self.arg) - ) - - def scanArguments(self): - while not self.hasScript(): - self._namespace.options.append(self.arg) - self.shiftToken( - 'Expected optional option argument', - 'got "{}" instead'.format(self.arg) - ) - - def scanScript(self): - self._exitOnMissingScript() - self._namespace.script = self.arg - - def scan(self): - self.scanOptions() - self.scanRepl() - self.scanArguments() - self.scanScript() - - -class Lexer(Scanner): - pass - - -def main(ast): - lexer = Lexer(ast) - lexer.scan() - lexer.logNamespace() - lexer.printNamespace() - - -if __name__ == '__main__': - logging.basicConfig( - filename='testing-cp.log', - filemode='w', - level=logging.DEBUG, - format='%(levelname)s: %(message)s' - ) - - main(argv) diff --git a/cp/cp/parse.py b/cp/cp/parse.py index abfaba3..f5b42e4 100644 --- a/cp/cp/parse.py +++ b/cp/cp/parse.py @@ -9,6 +9,8 @@ # https://docs.python.org/2/library/timeit.html # https://docs.python.org/2/library/sys.html#sys.platform # https://docs.python.org/2/library/string.html#formatstrings +# https://docs.python.org/3.6/howto/argparse.html +# https://docs.python.org/3.6/library/argparse.html # from __future__ import absolute_import from __future__ import print_function @@ -17,8 +19,8 @@ from subprocess import call from time import time from sys import platform +import argparse import logging -from .lexer import Lexer __all__ = ("Parser",) @@ -26,6 +28,7 @@ class Base(object): def __init__(self): + self._parser = None self._lexer = None self._namespace = dict() self._command = list() @@ -33,16 +36,19 @@ def __init__(self): self._exitCode = None self._clock = None - def setLexer(self, ast): - self._lexer = Lexer(ast) - self._lexer.scan() - self._lexer.logNamespace() + def setLexer(self, *args, **kwargs): + self._parser = argparse.ArgumentParser(**kwargs) + self._parser.add_argument('-f', '--file', help="pipes 'stdout' and 'stderr' to the given file") + self._parser.add_argument('-p', '--pause', action='store_true', default=False, help="prompt client for 'stdin'") + self._parser.add_argument('repl', help="the interpreter used to execute 'args'") + self._parser.add_argument('args', nargs='+', help="the arugments that are passed to 'repl'") + self._lexer = self._parser.parse_args(*args[1:]) def getLexer(self): return self._lexer def setNamespace(self): - self._namespace = self._lexer.getNamespace() + self._namespace = vars(self._lexer) def getNamespace(self): return self._namespace @@ -90,21 +96,22 @@ class Command(Base): def _setRepl(self): self._command.append(self._namespace['repl']) - def _setOptions(self): - for option in self._namespace['options']: - self._command.append(option) - - def _setScript(self): - self._command.append(self._namespace['script']) + def _setArgs(self): + for arg in self._namespace['args']: + self._command.append(arg) def _logCommand(self): logging.debug('command: %s', self._command) + def _logNamespace(self): + for key, value in self._namespace.items(): + logging.debug('%s: %s', key, value) + def setCommand(self): self._setRepl() - self._setOptions() - self._setScript() + self._setArgs() self._logCommand() + self._logNamespace() def getCommand(self): return self._command @@ -112,13 +119,13 @@ def getCommand(self): class Call(Base): def pipeCall(self, mode='a'): - with open(self._namespace['path'], mode) as f: + with open(self._namespace['file'], mode) as stream: self._clock = time() - self._exitCode = call(self._command, stdout=f, stderr=f) + self._exitCode = call(self._command, stdout=stream, stderr=stream) self._clock = time() - self._clock f.close() - def call(self, mode='a'): + def call(self): self._clock = time() self._exitCode = call(self._command) self._clock = time() - self._clock diff --git a/cp/main.py b/cp/main.py index 05cacdd..28b093a 100644 --- a/cp/main.py +++ b/cp/main.py @@ -32,9 +32,10 @@ from cp.parse import Parser from cp.utils import clear -version_info = "v0.2.0" if __name__ == '__main__': + version_info = "v0.3.0" + parser = Parser() parser.setLogPath() @@ -54,7 +55,7 @@ parser.logLogPath() - parser.setLexer(argv) + parser.setLexer(argv, description='Handles arbitrary arguments to be executed.') parser.setNamespace() @@ -67,7 +68,7 @@ if namespace['file']: parser.pipeCall('a') else: - parser.call('a') + parser.call() parser.logClock() diff --git a/lib/terminal.js b/lib/terminal.js index 6e4b797..add33ac 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -263,20 +263,20 @@ class SpawnWrapper extends Base { spawnArgs(model) { if (model.pause && !model.pipeFile) { return [this.object.call, this.object.script, - '--pause', ...model.args + '--pause', '--', ...model.args ]; } if (!model.pause && model.pipeFile) { return [this.object.call, this.object.script, - '--file', model.pipePath, ...model.args + '--file', model.pipePath, '--', ...model.args ]; } if (model.pause && model.pipeFile) { return [this.object.call, this.object.script, - '-p', '-f', model.pipePath, ...model.args + '-p', '-f', model.pipePath, '--', ...model.args ]; } - return [this.object.call, this.object.script, ...model.args]; + return [this.object.call, this.object.script, '--', ...model.args]; } spawnWin32(model) { From e5fcf5bda1c207dcfb27e35d0b41e770f106c879 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Sat, 18 Nov 2017 20:10:47 +0800 Subject: [PATCH 64/77] Handle async editor.save() --- CHANGELOG.md | 3 +++ lib/atom-python-run.js | 30 +++++++++++++----------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fb121c..835cc21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.9.4 +* Fixed inconsistency due to editor.save becoming async + ## 0.8.1 * Fixed python3 import error diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index b06f523..7c5422b 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -8,23 +8,20 @@ module.exports = { this.subscriptions = new CompositeDisposable(); - function runF5() { - run({ 'command': atom.config.get('atom-python-run.f5Command'), - 'pause': atom.config.get('atom-python-run.f5Pause'), - }); - } - - function runF6() { - run({ - 'command': atom.config.get('atom-python-run.f6Command'), - 'pause': atom.config.get('atom-python-run.f6Pause'), - }); - } - let disposables = [ - atom.commands.add('atom-text-editor', 'Python run: run-f5', runF5), - atom.commands.add('atom-text-editor', 'Python run: run-f6', runF6) + atom.commands.add('atom-text-editor', 'Python run: run-f5', saveAndRun.bind(this, 'f5')), + atom.commands.add('atom-text-editor', 'Python run: run-f6', saveAndRun.bind(this, 'f6')), ]; + + function saveAndRun(key) { + console.log(`${key} pressed`) + atom.workspace.getActiveTextEditor().save().then(() => { + run({ + 'command': atom.config.get(`atom-python-run.${key}Command`), + 'pause': atom.config.get(`atom-python-run.${key}Pause`), + }); + }); + } function run(config) { let shell, spawn, tty; @@ -47,8 +44,7 @@ module.exports = { ); } - editor.save(); - + args.forEach((item, index, arr) => { arr[index] = format(item, { 'file': editor.buffer.file.path, From 2fe7e34d20e0a9e39b31d0138914c1e33101424c Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Sat, 18 Nov 2017 20:19:03 +0800 Subject: [PATCH 65/77] Prepare 0.9.4 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b000ccd..cee5758 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.9.3", + "version": "0.9.4", "description": "Run a python source file.", "keywords": [ "python" From bbf752e82e1420a5dbd7d7de023642c926a1a182 Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Mon, 20 Nov 2017 17:33:13 -0500 Subject: [PATCH 66/77] patch for issue #64 with uncaught error: cant save a buffer with no file --- lib/atom-python-run.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index 7c5422b..e687ce5 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -12,15 +12,20 @@ module.exports = { atom.commands.add('atom-text-editor', 'Python run: run-f5', saveAndRun.bind(this, 'f5')), atom.commands.add('atom-text-editor', 'Python run: run-f6', saveAndRun.bind(this, 'f6')), ]; - + function saveAndRun(key) { - console.log(`${key} pressed`) - atom.workspace.getActiveTextEditor().save().then(() => { - run({ - 'command': atom.config.get(`atom-python-run.${key}Command`), - 'pause': atom.config.get(`atom-python-run.${key}Pause`), + try { + console.log(`${key} pressed`) + atom.workspace.getActiveTextEditor().save().then(() => { + run({ + 'command': atom.config.get(`atom-python-run.${key}Command`), + 'pause': atom.config.get(`atom-python-run.${key}Pause`), + }); }); - }); + } catch(error) { + atom.notifications.addError(`atom-python-run: \`BufferException${error}\`: You failed to create the file before attempting to execute it. You must create the file first and then execute it after it has been created.`); + return; + } } function run(config) { @@ -44,7 +49,7 @@ module.exports = { ); } - + args.forEach((item, index, arr) => { arr[index] = format(item, { 'file': editor.buffer.file.path, From 5862a3d63a248242066bbcac6e171075022e22be Mon Sep 17 00:00:00 2001 From: "Infinity@vipl.ict.ac" <573679956@qq.com> Date: Tue, 21 Nov 2017 11:09:35 +0800 Subject: [PATCH 67/77] Prepare 0.9.5 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cee5758..6b5eb2b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.9.4", + "version": "0.9.5", "description": "Run a python source file.", "keywords": [ "python" From a06ae23172e3ec554ed7766eb10ac101cff18f9f Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 5 Dec 2017 18:12:36 -0500 Subject: [PATCH 68/77] updated readme Updated README to improve navigation, add information, and reflect current repo status. --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 6fb244a..58d7264 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,22 @@ Run your python (.py) source file using `F5` or `F6`! ![](https://cloud.githubusercontent.com/assets/2712675/18710388/9a665ed8-8037-11e6-803a-35e4555e89d0.jpg) -# Prerequisite +# Contents +- [Prerequisite](https://github.com/foreshadow/atom-python-run#prerequisite) +- [Method of use](https://github.com/foreshadow/atom-python-run#method-of-use) +- [Features](https://github.com/foreshadow/atom-python-run#features) +- [Settings](https://github.com/foreshadow/atom-python-run#settings) +- [Compatability](https://github.com/foreshadow/atom-python-run#compatibility) +- [Documentation](https://github.com/foreshadow/atom-python-run/wiki) +- [Issues](https://github.com/foreshadow/atom-python-run#issues) +- [Logs](https://github.com/foreshadow/atom-python-run#logs) +- [Development](https://github.com/foreshadow/atom-python-run#development) -- Add the directory of `python` (or the intended interpreter) to ```PATH```. +# Prerequisite -- Filter file extensions (unset by default) (optional) +- Atom Text Editor (nightly or latest stable release) +- Python 2 and/or 3 +- Add Python (and any other interpreters) to the `PATH` environment variable. # Method of use @@ -20,7 +31,6 @@ Run your python (.py) source file using `F5` or `F6`! 2. Hit `F5` or `F6` to run. - **It will save the file in current editor immediately without a confirmation, be aware.** - # Features - Using `python` @@ -32,8 +42,8 @@ Run your python (.py) source file using `F5` or `F6`! # Settings - Extension filter - - It accepts all extension by default (empty value), you can change it into `.py` - - or an array `.py, .something-else` + - Accepts all file extensions by default (empty value), you can change it into `.py` + - or an array `.py, .js, .something-else` - Command - You can hack it using these variant, or @@ -42,9 +52,22 @@ Run your python (.py) source file using `F5` or `F6`! - `python -i {file}` - Run with idle, and you may need the next setting. - `pythonw C:\python27\Lib\idlelib\idle.pyw -r {file}` - -- Exit immediately - - You can disable the default cp wrapper if you don't want it. + - Pass arguments + - `python {file} these are arguments for file` + - Pause + - Show elapsed time and pause `cp` + - Exit immediately + - You can disable the default cp wrapper if you don't want it. +- Pipe to File + - Redirect standard output to a file instead of the screen + - Name the path and file to write to + - `/some/file/path/to/my-programs-output.txt` + - Create the file in the current working directory by omitting a path + - `my-programs-output.txt` +- Terminal + - Left empty by default (uses the systems default terminal) + - Choose the type of terminal you'd like to use instead + - `terminator, -x` # Compatibility @@ -55,20 +78,28 @@ Run your python (.py) source file using `F5` or `F6`! - Global python is the default interpreter - Execute using any interpreter - Pass options to the given interpreter + - Pass arguments to the program to be executed - Python 2 and 3 - Note: If you have problems executing, you can install a global version of latest `python2.7.x` (even if you have `python3.x.x` installed). Please report any `python3` issues if you want to avoid installing a global `python2` version. # Issues -- The new version of atom-python-run fully supports logging. - - Please post these logs with along with issue you're experiencing. - - Note that there are two logs in case the latter fails. The first log is the console log and the second log is created by the cp main.py executable. +- Before newing an issue, check to see if someone else is experiencing any related issues. +- Check to see if any issues that were closed resemble your issue and re-open it addressing that you're experiencing a similar issue. +- Provide details about your issue, such as errors and/or logs. +- Provide reproduction steps (we can't help you if we don't know how to reproduce the error!). + +# Logs +- The `atom-python-run` plug-in fully supports logging. + - Please post these logs along with any issue you're experiencing (we can't help you if we don't know what's happening!). + - Note that there are 2 logs in case the latter fails. The first log is the console log and the second log is created by the cp main.py executable. - How to access Atoms Console Log - Windows/Linux - Ctrl + Shift + I - Mac OS X - Cmd + Alt + I + - Click on the `console` tab - Copy and paste the console output along with your post. - How to access `cp`'s built-in log @@ -79,12 +110,13 @@ Run your python (.py) source file using `F5` or `F6`! 4. Locate, Open, and Copy the contents of the cp.log file along with your post. - Mac OS X/Linux 1. Open a terminal window. - `$ cat ~/.atom/packages/atom-python-run/cp.log` - 2. Copy and paste the contents along with your post. + 2. `$ cat ~/.atom/packages/atom-python-run/cp.log` + 3. Copy and paste the contents along with your post. - NOTE: If the `cp.log` file is missing, empty, or inaccurate, please note that this was case in your post. -- Detailed issues are well presented issues. This will help us locate your issue. +- Detailed issues are well presented issues. This will help us remedy the problem. +# Development - New an issue if you have any idea of new features. ![A screenshot of your package](https://f.cloud.github.com/assets/69169/2290250/c35d867a-a017-11e3-86be-cd7c5bf3ff9b.gif) From 50a459b29b761575d89b1085a99b7bba87473709 Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Thu, 21 Dec 2017 23:59:41 -0500 Subject: [PATCH 69/77] patch for TypeError and clean Exception catching for issues #67 and #64 --- lib/atom-python-run.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index e687ce5..e639eae 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -15,15 +15,21 @@ module.exports = { function saveAndRun(key) { try { - console.log(`${key} pressed`) - atom.workspace.getActiveTextEditor().save().then(() => { + console.log(`${key} pressed`); + // save() returns a promise + let save = atom.workspace.getActiveTextEditor().save(); + // Atom expects that promise to be resolved or rejected + return save.then(() => { run({ 'command': atom.config.get(`atom-python-run.${key}Command`), 'pause': atom.config.get(`atom-python-run.${key}Pause`), }); }); } catch(error) { - atom.notifications.addError(`atom-python-run: \`BufferException${error}\`: You failed to create the file before attempting to execute it. You must create the file first and then execute it after it has been created.`); + // the promise failed and the exception was caught + console.log(`atom-python-run: saveAndRun: ${error}`); + atom.notifications.addError(`atom-python-run: \`${error}\`.`); + // nothing to do, so we return undefined here return; } } @@ -49,7 +55,6 @@ module.exports = { ); } - args.forEach((item, index, arr) => { arr[index] = format(item, { 'file': editor.buffer.file.path, From a119757552d13ba41b15c92a95b9cb2f7b53b36e Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Sat, 20 Jan 2018 20:50:24 +0800 Subject: [PATCH 70/77] Update atom-python-run.js --- lib/atom-python-run.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/atom-python-run.js b/lib/atom-python-run.js index e639eae..17b4c47 100644 --- a/lib/atom-python-run.js +++ b/lib/atom-python-run.js @@ -18,13 +18,20 @@ module.exports = { console.log(`${key} pressed`); // save() returns a promise let save = atom.workspace.getActiveTextEditor().save(); - // Atom expects that promise to be resolved or rejected - return save.then(() => { + if (save) { + // Atom expects that promise to be resolved or rejected + return save.then(() => { + run({ + 'command': atom.config.get(`atom-python-run.${key}Command`), + 'pause': atom.config.get(`atom-python-run.${key}Pause`), + }); + }); + } else { // editor.save() will be sync and returns null on atom v1.18 and older run({ 'command': atom.config.get(`atom-python-run.${key}Command`), 'pause': atom.config.get(`atom-python-run.${key}Pause`), }); - }); + } } catch(error) { // the promise failed and the exception was caught console.log(`atom-python-run: saveAndRun: ${error}`); From a472ecb8e953e6457b68ff0ae1e4e498e5fe115c Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Sat, 20 Jan 2018 21:02:39 +0800 Subject: [PATCH 71/77] Prepare 0.9.6 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6b5eb2b..afc0888 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.9.5", + "version": "0.9.6", "description": "Run a python source file.", "keywords": [ "python" From 39aa2a4ae23d46f07eccdc46ae7b47d5b52d0726 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Sat, 3 Feb 2018 16:53:17 +0800 Subject: [PATCH 72/77] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 58d7264..8e64acd 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,15 @@ Run your python (.py) source file using `F5` or `F6`! ![](https://cloud.githubusercontent.com/assets/2712675/18710388/9a665ed8-8037-11e6-803a-35e4555e89d0.jpg) # Contents -- [Prerequisite](https://github.com/foreshadow/atom-python-run#prerequisite) -- [Method of use](https://github.com/foreshadow/atom-python-run#method-of-use) -- [Features](https://github.com/foreshadow/atom-python-run#features) -- [Settings](https://github.com/foreshadow/atom-python-run#settings) -- [Compatability](https://github.com/foreshadow/atom-python-run#compatibility) +- [Prerequisite](#prerequisite) +- [Method of use](#method-of-use) +- [Features](#features) +- [Settings](#settings) +- [Compatability](#compatibility) - [Documentation](https://github.com/foreshadow/atom-python-run/wiki) -- [Issues](https://github.com/foreshadow/atom-python-run#issues) -- [Logs](https://github.com/foreshadow/atom-python-run#logs) -- [Development](https://github.com/foreshadow/atom-python-run#development) +- [Issues](#issues) +- [Logs](#logs) +- [Development](#development) # Prerequisite From 46538e9e9189f701b0110515b51e4dd5d66b2cfe Mon Sep 17 00:00:00 2001 From: Hydridity Date: Wed, 28 Feb 2018 13:08:09 +0100 Subject: [PATCH 73/77] added default 'mate-terminal' for linux mint mate release --- lib/terminal.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/terminal.js b/lib/terminal.js index add33ac..5f42f98 100644 --- a/lib/terminal.js +++ b/lib/terminal.js @@ -56,6 +56,8 @@ class LinuxSessionWrapper extends Base { case 'gnome-fallback': case 'cinnamon': return "gnome-terminal"; + case 'mate': + return "mate-terminal"; case 'xfce': return "xfce4-terminal"; case 'kde-plasma': @@ -79,6 +81,8 @@ class LinuxSessionWrapper extends Base { return "konsole"; case 'LXDE': return "lxterminal"; + case 'MATE': + return "mate-terminal"; default: return "xterm"; } @@ -98,6 +102,7 @@ class LinuxSessionWrapper extends Base { // determine the shells default execution option switch (shell) { case 'gnome-terminal': + case 'mate-terminal': case 'xfce4-terminal': return "-x"; case 'konsole': From 0f8e143198ce5486f9267c273f9ec23058cac76f Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Tue, 3 Apr 2018 15:51:12 -0400 Subject: [PATCH 74/77] removed sections from readme.md in favor of wiki The Wiki has sections detailing most of what the README.md contains. The README.md has been modified to direct the majority of users to the Wiki instead to avoid confusion, repetition, and add clarity with visual aides. --- README.md | 83 ++++++++++++------------------------------------------- 1 file changed, 18 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 8e64acd..4902b67 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,10 @@ Run your python (.py) source file using `F5` or `F6`! # Contents - [Prerequisite](#prerequisite) -- [Method of use](#method-of-use) +- [Documentation](https://github.com/foreshadow/atom-python-run/wiki) - [Features](#features) -- [Settings](#settings) - [Compatability](#compatibility) -- [Documentation](https://github.com/foreshadow/atom-python-run/wiki) - [Issues](#issues) -- [Logs](#logs) - [Development](#development) # Prerequisite @@ -25,11 +22,17 @@ Run your python (.py) source file using `F5` or `F6`! - Python 2 and/or 3 - Add Python (and any other interpreters) to the `PATH` environment variable. -# Method of use +# Documentation + +This project has been documented in a fair amount of detail over time. This documentation can be found in the [Wiki](https://github.com/foreshadow/atom-python-run/wiki). + +**Everyone** should take the time to reveiw the [Wiki README](https://github.com/foreshadow/atom-python-run/wiki/00-Readme) at the bare minimum. It details an overview on how to handle issues, use different versions, and includes links to primary sections of the Wiki. + +**Everyone** should also take the time to review the Wiki section [How do I use atom-python-run?](https://github.com/foreshadow/atom-python-run/wiki/12-How-Do-I-Use-atom-python-run). It covers everything from installation, to configuration, logging, and much more. You just might be surprised by what you can do with atom-python-run. -1. Open a source file. -2. Hit `F5` or `F6` to run. - - **It will save the file in current editor immediately without a confirmation, be aware.** +You should have the basics after having covered both the *README* and *How Do I use atom-python-run?* sections. Most FAQ's can be resolved by simply reading them. The guides provided should allow us to help you with what ever issue you're facing. + +**NOTE**: Be sure to read the [Wiki](https://github.com/foreshadow/atom-python-run/wiki) and the [Wiki README](https://github.com/foreshadow/atom-python-run/wiki/00-Readme) before reporting an issue or making a pull request. A lot of time has been put in to it to help you the user (or dev) get started and on your way. # Features @@ -39,36 +42,6 @@ Run your python (.py) source file using `F5` or `F6`! - Shows return value and execution time - It is a rough time based on real time rather than CPU kernel / user time -# Settings - -- Extension filter - - Accepts all file extensions by default (empty value), you can change it into `.py` - - or an array `.py, .js, .something-else` - -- Command - - You can hack it using these variant, or - - `{file}` = `{dir}/{name}{ext}` - - Run with interactive mode, or - - `python -i {file}` - - Run with idle, and you may need the next setting. - - `pythonw C:\python27\Lib\idlelib\idle.pyw -r {file}` - - Pass arguments - - `python {file} these are arguments for file` - - Pause - - Show elapsed time and pause `cp` - - Exit immediately - - You can disable the default cp wrapper if you don't want it. -- Pipe to File - - Redirect standard output to a file instead of the screen - - Name the path and file to write to - - `/some/file/path/to/my-programs-output.txt` - - Create the file in the current working directory by omitting a path - - `my-programs-output.txt` -- Terminal - - Left empty by default (uses the systems default terminal) - - Choose the type of terminal you'd like to use instead - - `terminator, -x` - # Compatibility - Cross Platform Compatible @@ -89,34 +62,14 @@ Run your python (.py) source file using `F5` or `F6`! - Provide details about your issue, such as errors and/or logs. - Provide reproduction steps (we can't help you if we don't know how to reproduce the error!). -# Logs -- The `atom-python-run` plug-in fully supports logging. - - Please post these logs along with any issue you're experiencing (we can't help you if we don't know what's happening!). - - Note that there are 2 logs in case the latter fails. The first log is the console log and the second log is created by the cp main.py executable. - -- How to access Atoms Console Log - - Windows/Linux - - Ctrl + Shift + I - - Mac OS X - - Cmd + Alt + I - - Click on the `console` tab - - Copy and paste the console output along with your post. - -- How to access `cp`'s built-in log - - Windows - 1. Open file explorer - 2. Click the location bar (where the file path usually is) - 3. Type in `%userprofile%\.atom\packages\atom-python-run` - 4. Locate, Open, and Copy the contents of the cp.log file along with your post. - - Mac OS X/Linux - 1. Open a terminal window. - 2. `$ cat ~/.atom/packages/atom-python-run/cp.log` - 3. Copy and paste the contents along with your post. - - NOTE: If the `cp.log` file is missing, empty, or inaccurate, please note that this was case in your post. - -- Detailed issues are well presented issues. This will help us remedy the problem. - # Development + +If you're a developer and are interested in this project you can find this repos API's in the [Wiki](https://github.com/foreshadow/atom-python-run/wiki). More specifically, you'll want to take a look at [How does the cp module work?](https://github.com/foreshadow/atom-python-run/wiki#how-does-the-cp-module-work) and [How does the terminal.js module work?](https://github.com/foreshadow/atom-python-run/wiki#how-does-the-terminal-module-work) sections of the Wiki. + +You can also just read the key source files +- [cp](https://github.com/foreshadow/atom-python-run/tree/master/cp) (cp is written in python) +- [terminal.js](https://github.com/foreshadow/atom-python-run/blob/master/lib/terminal.js) +- [atom-python-run.js](https://github.com/foreshadow/atom-python-run/blob/master/lib/atom-python-run.js) - New an issue if you have any idea of new features. ![A screenshot of your package](https://f.cloud.github.com/assets/69169/2290250/c35d867a-a017-11e3-86be-cd7c5bf3ff9b.gif) From 1eb5ebf71a39f5a61f4f668422b777287db8125f Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Tue, 3 Apr 2018 15:55:19 -0400 Subject: [PATCH 75/77] fixed README.md link for Documenation section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4902b67..a2ef9d7 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Run your python (.py) source file using `F5` or `F6`! # Contents - [Prerequisite](#prerequisite) -- [Documentation](https://github.com/foreshadow/atom-python-run/wiki) +- [Documentation](#documentation) - [Features](#features) - [Compatability](#compatibility) - [Issues](#issues) From 8bbd8081e308b13bc56bcf5e911688d65a8b67d2 Mon Sep 17 00:00:00 2001 From: xovertheyearsx Date: Tue, 3 Apr 2018 16:02:00 -0400 Subject: [PATCH 76/77] refactored README.md so it looks pretty --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a2ef9d7..a933ed2 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ Run your python (.py) source file using `F5` or `F6`! # Contents - [Prerequisite](#prerequisite) -- [Documentation](#documentation) - [Features](#features) - [Compatability](#compatibility) +- [Documentation](#documentation) - [Issues](#issues) - [Development](#development) @@ -22,18 +22,6 @@ Run your python (.py) source file using `F5` or `F6`! - Python 2 and/or 3 - Add Python (and any other interpreters) to the `PATH` environment variable. -# Documentation - -This project has been documented in a fair amount of detail over time. This documentation can be found in the [Wiki](https://github.com/foreshadow/atom-python-run/wiki). - -**Everyone** should take the time to reveiw the [Wiki README](https://github.com/foreshadow/atom-python-run/wiki/00-Readme) at the bare minimum. It details an overview on how to handle issues, use different versions, and includes links to primary sections of the Wiki. - -**Everyone** should also take the time to review the Wiki section [How do I use atom-python-run?](https://github.com/foreshadow/atom-python-run/wiki/12-How-Do-I-Use-atom-python-run). It covers everything from installation, to configuration, logging, and much more. You just might be surprised by what you can do with atom-python-run. - -You should have the basics after having covered both the *README* and *How Do I use atom-python-run?* sections. Most FAQ's can be resolved by simply reading them. The guides provided should allow us to help you with what ever issue you're facing. - -**NOTE**: Be sure to read the [Wiki](https://github.com/foreshadow/atom-python-run/wiki) and the [Wiki README](https://github.com/foreshadow/atom-python-run/wiki/00-Readme) before reporting an issue or making a pull request. A lot of time has been put in to it to help you the user (or dev) get started and on your way. - # Features - Using `python` @@ -56,6 +44,18 @@ You should have the basics after having covered both the *README* and *How Do I - Python 2 and 3 - Note: If you have problems executing, you can install a global version of latest `python2.7.x` (even if you have `python3.x.x` installed). Please report any `python3` issues if you want to avoid installing a global `python2` version. +# Documentation + +This project has been documented in a fair amount of detail over time. This documentation can be found in the [Wiki](https://github.com/foreshadow/atom-python-run/wiki). + +**Everyone** should take the time to reveiw the [Wiki README](https://github.com/foreshadow/atom-python-run/wiki/00-Readme) at the bare minimum. It details an overview on how to handle issues, use different versions, and includes links to primary sections of the Wiki. + +**Everyone** should also take the time to review the Wiki section [How do I use atom-python-run?](https://github.com/foreshadow/atom-python-run/wiki/12-How-Do-I-Use-atom-python-run). It covers everything from installation, to configuration, logging, and much more. You just might be surprised by what you can do with atom-python-run. + +You should have the basics after having covered both the *README* and *How Do I use atom-python-run?* sections. Most FAQ's can be resolved by simply reading them. The guides provided should allow us to help you with what ever issue you're facing. + +**NOTE**: Be sure to read the [Wiki](https://github.com/foreshadow/atom-python-run/wiki) and the [Wiki README](https://github.com/foreshadow/atom-python-run/wiki/00-Readme) before reporting an issue or making a pull request. A lot of time has been put in to it to help you the user (or dev) get started and on your way. + # Issues - Before newing an issue, check to see if someone else is experiencing any related issues. - Check to see if any issues that were closed resemble your issue and re-open it addressing that you're experiencing a similar issue. From 62dc57cbbfadb20b5d45e92efd100db243c362c3 Mon Sep 17 00:00:00 2001 From: Infinity <573679956@qq.com> Date: Tue, 31 Jul 2018 10:51:04 +0800 Subject: [PATCH 77/77] Prepare 0.9.7 release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index afc0888..606ca15 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom-python-run", "main": "./lib/atom-python-run.js", - "version": "0.9.6", + "version": "0.9.7", "description": "Run a python source file.", "keywords": [ "python"